[flutter_tools] Deferred components build system (#76192)
This commit is contained in:
parent
faabc9ab46
commit
ee41782732
@ -519,7 +519,7 @@ Future<void> _flutterBuild(
|
||||
final File file = File(path.join(flutterRoot, relativePathToApplication, 'perf.json'));
|
||||
if (!_allTargetsCached(file)) {
|
||||
print('${red}Not all build targets cached after second run.$reset');
|
||||
print('The target performance data was: ${file.readAsStringSync()}');
|
||||
print('The target performance data was: ${file.readAsStringSync().replaceAll('},', '},\n')}');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +134,14 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.hasProperty('deferred-component-names')) {
|
||||
String[] componentNames = project.property('deferred-component-names').split(',').collect {":${it}"}
|
||||
project.android {
|
||||
dynamicFeatures = componentNames
|
||||
}
|
||||
}
|
||||
|
||||
getTargetPlatforms().each { targetArch ->
|
||||
String abiValue = PLATFORM_ARCH_MAP[targetArch]
|
||||
project.android {
|
||||
@ -173,17 +181,23 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
matchingFallbacks = ["debug", "release"]
|
||||
}
|
||||
}
|
||||
release {
|
||||
// Enables code shrinking, obfuscation, and optimization for only
|
||||
// your project's release build type.
|
||||
minifyEnabled true
|
||||
// Enables resource shrinking, which is performed by the
|
||||
// Android Gradle plugin.
|
||||
// NOTE: The resource shrinker can't be used for libraries.
|
||||
shrinkResources isBuiltAsApp(project)
|
||||
// Fallback to `android/app/proguard-rules.pro`.
|
||||
// This way, custom Proguard rules can be configured as needed.
|
||||
proguardFiles project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro"
|
||||
// TODO(garyq): Shrinking is only false for multi apk split aot builds, where shrinking is not allowed yet.
|
||||
// This limitation has been removed experimentally in gradle plugin version 4.2, so we can remove
|
||||
// this check when we upgrade to 4.2+ gradle. Currently, deferred components apps may see
|
||||
// increased app size due to this.
|
||||
if (shouldShrinkResources(project)) {
|
||||
release {
|
||||
// Enables code shrinking, obfuscation, and optimization for only
|
||||
// your project's release build type.
|
||||
minifyEnabled true
|
||||
// Enables resource shrinking, which is performed by the
|
||||
// Android Gradle plugin.
|
||||
// NOTE: The resource shrinker can't be used for libraries.
|
||||
shrinkResources isBuiltAsApp(project)
|
||||
// Fallback to `android/app/proguard-rules.pro`.
|
||||
// This way, custom Proguard rules can be configured as needed.
|
||||
proguardFiles project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,6 +214,13 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
project.android.buildTypes.all this.&addFlutterDependencies
|
||||
}
|
||||
|
||||
private static Boolean shouldShrinkResources(Project project) {
|
||||
if (project.hasProperty("shrink")) {
|
||||
return project.property("shrink").toBoolean()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the dependencies required by the Flutter project.
|
||||
* This includes:
|
||||
@ -706,6 +727,14 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
if (project.hasProperty('code-size-directory')) {
|
||||
codeSizeDirectoryValue = project.property('code-size-directory')
|
||||
}
|
||||
Boolean deferredComponentsValue = false
|
||||
if (project.hasProperty('deferred-components')) {
|
||||
deferredComponentsValue = project.property('deferred-components').toBoolean()
|
||||
}
|
||||
Boolean validateDeferredComponentsValue = true
|
||||
if (project.hasProperty('validate-deferred-components')) {
|
||||
validateDeferredComponentsValue = project.property('validate-deferred-components').toBoolean()
|
||||
}
|
||||
def targetPlatforms = getTargetPlatforms()
|
||||
def addFlutterDeps = { variant ->
|
||||
if (shouldSplitPerAbi()) {
|
||||
@ -747,6 +776,8 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
bundleSkSLPath bundleSkSLPathValue
|
||||
performanceMeasurementFile performanceMeasurementFileValue
|
||||
codeSizeDirectory codeSizeDirectoryValue
|
||||
deferredComponents deferredComponentsValue
|
||||
validateDeferredComponents validateDeferredComponentsValue
|
||||
doLast {
|
||||
project.exec {
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
@ -810,7 +841,8 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
processResources.dependsOn(copyFlutterAssetsTask)
|
||||
}
|
||||
return copyFlutterAssetsTask
|
||||
}
|
||||
} // end def addFlutterDeps
|
||||
|
||||
if (isFlutterAppProject()) {
|
||||
project.android.applicationVariants.all { variant ->
|
||||
Task assembleTask = getAssembleTask(variant)
|
||||
@ -883,7 +915,7 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
// | ----------------- | ----------------------------- |
|
||||
// | Build Variant | Flutter Equivalent Variant |
|
||||
// | ----------------- | ----------------------------- |
|
||||
// | freeRelease | relese |
|
||||
// | freeRelease | release |
|
||||
// | freeDebug | debug |
|
||||
// | freeDevelop | debug |
|
||||
// | profile | profile |
|
||||
@ -961,6 +993,10 @@ abstract class BaseFlutterTask extends DefaultTask {
|
||||
@Optional @Input
|
||||
String codeSizeDirectory;
|
||||
String performanceMeasurementFile;
|
||||
@Optional @Input
|
||||
Boolean deferredComponents
|
||||
@Optional @Input
|
||||
Boolean validateDeferredComponents
|
||||
|
||||
@OutputFiles
|
||||
FileCollection getDependenciesFiles() {
|
||||
@ -985,6 +1021,8 @@ abstract class BaseFlutterTask extends DefaultTask {
|
||||
String[] ruleNames;
|
||||
if (buildMode == "debug") {
|
||||
ruleNames = ["debug_android_application"]
|
||||
} else if (deferredComponents) {
|
||||
ruleNames = targetPlatformValues.collect { "android_aot_deferred_components_bundle_${buildMode}_$it" }
|
||||
} else {
|
||||
ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" }
|
||||
}
|
||||
|
@ -38,5 +38,7 @@ abstract class AndroidBuilder {
|
||||
@required FlutterProject project,
|
||||
@required AndroidBuildInfo androidBuildInfo,
|
||||
@required String target,
|
||||
bool validateDeferredComponents = true,
|
||||
bool deferredComponentsEnabled = false,
|
||||
});
|
||||
}
|
||||
|
@ -30,10 +30,17 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
|
||||
/// When [exitOnFail] is set to true, the [handleResults] and [attemptToolExit]
|
||||
/// methods will exit the tool when this validator detects a recommended
|
||||
/// change. This defaults to true.
|
||||
DeferredComponentsGenSnapshotValidator(Environment env, {
|
||||
DeferredComponentsGenSnapshotValidator(this.env, {
|
||||
bool exitOnFail = true,
|
||||
String title,
|
||||
}) : super(env, exitOnFail: exitOnFail, title: title);
|
||||
}) : super(env.projectDir, env.logger, exitOnFail: exitOnFail, title: title);
|
||||
|
||||
/// The build environment that should be used to find the input files to run
|
||||
/// checks against.
|
||||
///
|
||||
/// The checks in this class are meant to be used as part of a build process,
|
||||
/// so an environment should be available.
|
||||
final Environment env;
|
||||
|
||||
// The key used to identify the metadata element as the loading unit id to
|
||||
// deferred component mapping.
|
||||
@ -58,8 +65,8 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
|
||||
/// Where loading unit 2 is included in componentA, loading unit 3 is included
|
||||
/// in componentB, and loading unit 4 is included in componentC.
|
||||
bool checkAppAndroidManifestComponentLoadingUnitMapping(List<DeferredComponent> components, List<LoadingUnit> generatedLoadingUnits) {
|
||||
final Directory androidDir = env.projectDir.childDirectory('android');
|
||||
inputs.add(env.projectDir.childFile('pubspec.yaml'));
|
||||
final Directory androidDir = projectDir.childDirectory('android');
|
||||
inputs.add(projectDir.childFile('pubspec.yaml'));
|
||||
|
||||
// We do not use the Xml package to handle the writing, as we do not want to
|
||||
// erase any user applied formatting and comments. The changes can be
|
||||
@ -106,8 +113,10 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
|
||||
mappingBuffer.write('$key:${mapping[key]},');
|
||||
}
|
||||
String encodedMapping = mappingBuffer.toString();
|
||||
// remove trailing comma.
|
||||
encodedMapping = encodedMapping.substring(0, encodedMapping.length - 1);
|
||||
// remove trailing comma if any
|
||||
if (encodedMapping.endsWith(',')) {
|
||||
encodedMapping = encodedMapping.substring(0, encodedMapping.length - 1);
|
||||
}
|
||||
// Check for existing metadata entry and see if needs changes.
|
||||
bool exists = false;
|
||||
bool modified = false;
|
||||
@ -163,7 +172,7 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
|
||||
/// considered new.
|
||||
bool checkAgainstLoadingUnitsCache(
|
||||
List<LoadingUnit> generatedLoadingUnits) {
|
||||
final List<LoadingUnit> cachedLoadingUnits = _parseLodingUnitsCache(env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName));
|
||||
final List<LoadingUnit> cachedLoadingUnits = _parseLodingUnitsCache(projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName));
|
||||
loadingUnitComparisonResults = <String, dynamic>{};
|
||||
final Set<LoadingUnit> unmatchedLoadingUnits = <LoadingUnit>{};
|
||||
final List<LoadingUnit> newLoadingUnits = <LoadingUnit>[];
|
||||
@ -276,7 +285,7 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
|
||||
/// deferred components.
|
||||
void writeLoadingUnitsCache(List<LoadingUnit> generatedLoadingUnits) {
|
||||
generatedLoadingUnits ??= <LoadingUnit>[];
|
||||
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
|
||||
final File cacheFile = projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
|
||||
outputs.add(cacheFile);
|
||||
ErrorHandlingFileSystem.deleteIfExists(cacheFile);
|
||||
cacheFile.createSync(recursive: true);
|
||||
|
@ -10,7 +10,7 @@ import 'package:xml/xml.dart';
|
||||
import '../base/deferred_component.dart';
|
||||
import '../base/error_handling_io.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../build_system/build_system.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../project.dart';
|
||||
import '../template.dart';
|
||||
@ -25,20 +25,18 @@ import 'deferred_components_validator.dart';
|
||||
class DeferredComponentsPrebuildValidator extends DeferredComponentsValidator {
|
||||
/// Constructs a validator instance.
|
||||
///
|
||||
/// The [env] property is used to locate the project files that are checked.
|
||||
///
|
||||
/// The [templatesDir] parameter is optional. If null, the tool's default
|
||||
/// templates directory will be used.
|
||||
///
|
||||
/// When [exitOnFail] is set to true, the [handleResults] and [attemptToolExit]
|
||||
/// methods will exit the tool when this validator detects a recommended
|
||||
/// change. This defaults to true.
|
||||
DeferredComponentsPrebuildValidator(Environment env, {
|
||||
DeferredComponentsPrebuildValidator(Directory projectDir, Logger logger, {
|
||||
bool exitOnFail = true,
|
||||
String title,
|
||||
Directory templatesDir,
|
||||
}) : _templatesDir = templatesDir,
|
||||
super(env, exitOnFail: exitOnFail, title: title);
|
||||
super(projectDir, logger, exitOnFail: exitOnFail, title: title);
|
||||
|
||||
final Directory _templatesDir;
|
||||
|
||||
@ -56,7 +54,7 @@ class DeferredComponentsPrebuildValidator extends DeferredComponentsValidator {
|
||||
/// This method does not check if the contents of either of the files are
|
||||
/// valid, as there are many ways that they can be validly configured.
|
||||
Future<bool> checkAndroidDynamicFeature(List<DeferredComponent> components) async {
|
||||
inputs.add(env.projectDir.childFile('pubspec.yaml'));
|
||||
inputs.add(projectDir.childFile('pubspec.yaml'));
|
||||
if (components == null || components.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
@ -64,7 +62,8 @@ class DeferredComponentsPrebuildValidator extends DeferredComponentsValidator {
|
||||
for (final DeferredComponent component in components) {
|
||||
final _DeferredComponentAndroidFiles androidFiles = _DeferredComponentAndroidFiles(
|
||||
name: component.name,
|
||||
env: env,
|
||||
projectDir: projectDir,
|
||||
logger: logger,
|
||||
templatesDir: _templatesDir
|
||||
);
|
||||
if (!androidFiles.verifyFilesExist()) {
|
||||
@ -106,8 +105,8 @@ class DeferredComponentsPrebuildValidator extends DeferredComponentsValidator {
|
||||
/// The string element's name attribute should be the component name with
|
||||
/// `Name` as a suffix, and the text contents should be the component name.
|
||||
bool checkAndroidResourcesStrings(List<DeferredComponent> components) {
|
||||
final Directory androidDir = env.projectDir.childDirectory('android');
|
||||
inputs.add(env.projectDir.childFile('pubspec.yaml'));
|
||||
final Directory androidDir = projectDir.childDirectory('android');
|
||||
inputs.add(projectDir.childFile('pubspec.yaml'));
|
||||
|
||||
// Add component name mapping to strings.xml
|
||||
final File stringRes = androidDir
|
||||
@ -202,7 +201,7 @@ class DeferredComponentsPrebuildValidator extends DeferredComponentsValidator {
|
||||
|
||||
/// Deletes all files inside of the validator's output directory.
|
||||
void clearOutputDir() {
|
||||
final Directory dir = env.projectDir.childDirectory('build').childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory);
|
||||
final Directory dir = projectDir.childDirectory('build').childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory);
|
||||
ErrorHandlingFileSystem.deleteIfExists(dir, recursive: true);
|
||||
}
|
||||
}
|
||||
@ -212,16 +211,18 @@ class DeferredComponentsPrebuildValidator extends DeferredComponentsValidator {
|
||||
class _DeferredComponentAndroidFiles {
|
||||
_DeferredComponentAndroidFiles({
|
||||
@required this.name,
|
||||
@required this.env,
|
||||
@required this.projectDir,
|
||||
this.logger,
|
||||
Directory templatesDir,
|
||||
}) : _templatesDir = templatesDir;
|
||||
|
||||
// The name of the deferred component.
|
||||
final String name;
|
||||
final Environment env;
|
||||
final Directory projectDir;
|
||||
final Logger logger;
|
||||
final Directory _templatesDir;
|
||||
|
||||
Directory get androidDir => env.projectDir.childDirectory('android');
|
||||
Directory get androidDir => projectDir.childDirectory('android');
|
||||
Directory get componentDir => androidDir.childDirectory(name);
|
||||
|
||||
File get androidManifestFile => componentDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml');
|
||||
@ -250,18 +251,18 @@ class _DeferredComponentAndroidFiles {
|
||||
Future<List<File>> _setupComponentFiles(Directory outputDir) async {
|
||||
Template template;
|
||||
if (_templatesDir != null) {
|
||||
final Directory templateComponentDir = _templatesDir.childDirectory('module${env.fileSystem.path.separator}android${env.fileSystem.path.separator}deferred_component');
|
||||
final Directory templateComponentDir = _templatesDir.childDirectory('module${globals.fs.path.separator}android${globals.fs.path.separator}deferred_component');
|
||||
template = Template(templateComponentDir, templateComponentDir, _templatesDir,
|
||||
fileSystem: env.fileSystem,
|
||||
fileSystem: globals.fs,
|
||||
templateManifest: null,
|
||||
logger: env.logger,
|
||||
logger: logger,
|
||||
templateRenderer: globals.templateRenderer,
|
||||
);
|
||||
} else {
|
||||
template = await Template.fromName('module${env.fileSystem.path.separator}android${env.fileSystem.path.separator}deferred_component',
|
||||
fileSystem: env.fileSystem,
|
||||
template = await Template.fromName('module${globals.fs.path.separator}android${globals.fs.path.separator}deferred_component',
|
||||
fileSystem: globals.fs,
|
||||
templateManifest: null,
|
||||
logger: env.logger,
|
||||
logger: logger,
|
||||
templateRenderer: globals.templateRenderer,
|
||||
);
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
import '../base/common.dart';
|
||||
import '../base/deferred_component.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../base/terminal.dart';
|
||||
import '../build_system/build_system.dart';
|
||||
import '../globals.dart' as globals;
|
||||
|
||||
/// A class to configure and run deferred component setup verification checks
|
||||
@ -21,10 +21,10 @@ import '../globals.dart' as globals;
|
||||
/// The results of each check are handled internally as they are not meant to
|
||||
/// be run isolated.
|
||||
abstract class DeferredComponentsValidator {
|
||||
DeferredComponentsValidator(this.env, {
|
||||
DeferredComponentsValidator(this.projectDir, this.logger, {
|
||||
this.exitOnFail = true,
|
||||
String title,
|
||||
}) : outputDir = env.projectDir
|
||||
}) : outputDir = projectDir
|
||||
.childDirectory('build')
|
||||
.childDirectory(kDeferredComponentsTempDirectory),
|
||||
inputs = <File>[],
|
||||
@ -34,12 +34,9 @@ abstract class DeferredComponentsValidator {
|
||||
modifiedFiles = <String>[],
|
||||
invalidFiles = <String, String>{},
|
||||
diffLines = <String>[];
|
||||
/// The build environment that should be used to find the input files to run
|
||||
/// checks against.
|
||||
///
|
||||
/// The checks in this class are meant to be used as part of a build process,
|
||||
/// so an environment should be available.
|
||||
final Environment env;
|
||||
|
||||
/// Logger to use for [displayResults] output.
|
||||
final Logger logger;
|
||||
|
||||
/// When true, failed checks and tasks will result in [attemptToolExit]
|
||||
/// triggering [throwToolExit].
|
||||
@ -54,6 +51,9 @@ abstract class DeferredComponentsValidator {
|
||||
/// The title printed at the top of the results of [displayResults]
|
||||
final String title;
|
||||
|
||||
/// The root directory of the flutter project.
|
||||
final Directory projectDir;
|
||||
|
||||
/// The temporary directory that the validator writes recommended files into.
|
||||
final Directory outputDir;
|
||||
|
||||
@ -111,20 +111,20 @@ abstract class DeferredComponentsValidator {
|
||||
/// All checks that are desired should be run before calling this method.
|
||||
void displayResults() {
|
||||
if (changesNeeded) {
|
||||
env.logger.printStatus(_thickDivider);
|
||||
env.logger.printStatus(title, indent: (_thickDivider.length - title.length) ~/ 2, emphasis: true);
|
||||
env.logger.printStatus(_thickDivider);
|
||||
logger.printStatus(_thickDivider);
|
||||
logger.printStatus(title, indent: (_thickDivider.length - title.length) ~/ 2, emphasis: true);
|
||||
logger.printStatus(_thickDivider);
|
||||
// Log any file reading/existence errors.
|
||||
if (invalidFiles.isNotEmpty) {
|
||||
env.logger.printStatus('Errors checking the following files:\n', emphasis: true);
|
||||
logger.printStatus('Errors checking the following files:\n', emphasis: true);
|
||||
for (final String key in invalidFiles.keys) {
|
||||
env.logger.printStatus(' - $key: ${invalidFiles[key]}\n');
|
||||
logger.printStatus(' - $key: ${invalidFiles[key]}\n');
|
||||
}
|
||||
}
|
||||
// Log diff file contents, with color highlighting
|
||||
if (diffLines != null && diffLines.isNotEmpty) {
|
||||
env.logger.printStatus('Diff between `android` and expected files:', emphasis: true);
|
||||
env.logger.printStatus('');
|
||||
logger.printStatus('Diff between `android` and expected files:', emphasis: true);
|
||||
logger.printStatus('');
|
||||
for (final String line in diffLines) {
|
||||
// We only care about diffs in files that have
|
||||
// counterparts.
|
||||
@ -137,62 +137,62 @@ abstract class DeferredComponentsValidator {
|
||||
} else if (line.startsWith('-')) {
|
||||
color = TerminalColor.red;
|
||||
}
|
||||
env.logger.printStatus(line, color: color);
|
||||
logger.printStatus(line, color: color);
|
||||
}
|
||||
env.logger.printStatus('');
|
||||
logger.printStatus('');
|
||||
}
|
||||
// Log any newly generated and modified files.
|
||||
if (generatedFiles.isNotEmpty) {
|
||||
env.logger.printStatus('Newly generated android files:', emphasis: true);
|
||||
logger.printStatus('Newly generated android files:', emphasis: true);
|
||||
for (final String filePath in generatedFiles) {
|
||||
final String shortenedPath = filePath.substring(env.projectDir.parent.path.length + 1);
|
||||
env.logger.printStatus(' - $shortenedPath', color: TerminalColor.grey);
|
||||
final String shortenedPath = filePath.substring(projectDir.parent.path.length + 1);
|
||||
logger.printStatus(' - $shortenedPath', color: TerminalColor.grey);
|
||||
}
|
||||
env.logger.printStatus('');
|
||||
logger.printStatus('');
|
||||
}
|
||||
if (modifiedFiles.isNotEmpty) {
|
||||
env.logger.printStatus('Modified android files:', emphasis: true);
|
||||
logger.printStatus('Modified android files:', emphasis: true);
|
||||
for (final String filePath in modifiedFiles) {
|
||||
final String shortenedPath = filePath.substring(env.projectDir.parent.path.length + 1);
|
||||
env.logger.printStatus(' - $shortenedPath', color: TerminalColor.grey);
|
||||
final String shortenedPath = filePath.substring(projectDir.parent.path.length + 1);
|
||||
logger.printStatus(' - $shortenedPath', color: TerminalColor.grey);
|
||||
}
|
||||
env.logger.printStatus('');
|
||||
logger.printStatus('');
|
||||
}
|
||||
if (generatedFiles.isNotEmpty || modifiedFiles.isNotEmpty) {
|
||||
env.logger.printStatus('''
|
||||
logger.printStatus('''
|
||||
The above files have been placed into `build/$kDeferredComponentsTempDirectory`,
|
||||
a temporary directory. The files should be reviewed and moved into the project's
|
||||
`android` directory.''');
|
||||
if (diffLines != null && diffLines.isNotEmpty && !globals.platform.isWindows) {
|
||||
env.logger.printStatus(r'''
|
||||
logger.printStatus(r'''
|
||||
|
||||
The recommended changes can be quickly applied by running:
|
||||
|
||||
$ patch -p0 < build/setup_deferred_components.diff
|
||||
''');
|
||||
}
|
||||
env.logger.printStatus('$_thinDivider\n');
|
||||
logger.printStatus('$_thinDivider\n');
|
||||
}
|
||||
// Log loading unit golden changes, if any.
|
||||
if (loadingUnitComparisonResults != null) {
|
||||
if ((loadingUnitComparisonResults['new'] as List<LoadingUnit>).isNotEmpty) {
|
||||
env.logger.printStatus('New loading units were found:', emphasis: true);
|
||||
logger.printStatus('New loading units were found:', emphasis: true);
|
||||
for (final LoadingUnit unit in loadingUnitComparisonResults['new'] as List<LoadingUnit>) {
|
||||
env.logger.printStatus(unit.toString(), color: TerminalColor.grey, indent: 2);
|
||||
logger.printStatus(unit.toString(), color: TerminalColor.grey, indent: 2);
|
||||
}
|
||||
env.logger.printStatus('');
|
||||
logger.printStatus('');
|
||||
}
|
||||
if ((loadingUnitComparisonResults['missing'] as Set<LoadingUnit>).isNotEmpty) {
|
||||
env.logger.printStatus('Previously existing loading units no longer exist:', emphasis: true);
|
||||
logger.printStatus('Previously existing loading units no longer exist:', emphasis: true);
|
||||
for (final LoadingUnit unit in loadingUnitComparisonResults['missing'] as Set<LoadingUnit>) {
|
||||
env.logger.printStatus(unit.toString(), color: TerminalColor.grey, indent: 2);
|
||||
logger.printStatus(unit.toString(), color: TerminalColor.grey, indent: 2);
|
||||
}
|
||||
env.logger.printStatus('');
|
||||
logger.printStatus('');
|
||||
}
|
||||
if (loadingUnitComparisonResults['match'] as bool) {
|
||||
env.logger.printStatus('No change in generated loading units.\n');
|
||||
logger.printStatus('No change in generated loading units.\n');
|
||||
} else {
|
||||
env.logger.printStatus('''
|
||||
logger.printStatus('''
|
||||
It is recommended to verify that the changed loading units are expected
|
||||
and to update the `deferred-components` section in `pubspec.yaml` to
|
||||
incorporate any changes. The full list of generated loading units can be
|
||||
@ -205,14 +205,14 @@ $_thinDivider\n''');
|
||||
}
|
||||
}
|
||||
// TODO(garyq): Add link to web tutorial/guide once it is written.
|
||||
env.logger.printStatus('''
|
||||
Setup verification can be skipped by passing the `--no-verify-deferred-components`
|
||||
logger.printStatus('''
|
||||
Setup verification can be skipped by passing the `--no-validate-deferred-components`
|
||||
flag, however, doing so may put your app at risk of not functioning even if the
|
||||
build is successful.
|
||||
$_thickDivider''');
|
||||
return;
|
||||
}
|
||||
env.logger.printStatus('$title passed.');
|
||||
logger.printStatus('$title passed.');
|
||||
}
|
||||
|
||||
void attemptToolExit() {
|
||||
|
@ -12,6 +12,7 @@ import 'package:xml/xml.dart';
|
||||
import '../artifacts.dart';
|
||||
import '../base/analyze_size.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/deferred_component.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
@ -242,6 +243,8 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
@required FlutterProject project,
|
||||
@required AndroidBuildInfo androidBuildInfo,
|
||||
@required String target,
|
||||
bool validateDeferredComponents = true,
|
||||
bool deferredComponentsEnabled = false,
|
||||
}) async {
|
||||
await buildGradleApp(
|
||||
project: project,
|
||||
@ -249,6 +252,8 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
target: target,
|
||||
isBuildingBundle: true,
|
||||
localGradleErrors: gradleErrors,
|
||||
validateDeferredComponents: validateDeferredComponents,
|
||||
deferredComponentsEnabled: deferredComponentsEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
@ -270,6 +275,8 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
@required bool isBuildingBundle,
|
||||
@required List<GradleHandledError> localGradleErrors,
|
||||
bool shouldBuildPluginAsAar = false,
|
||||
bool validateDeferredComponents = true,
|
||||
bool deferredComponentsEnabled = false,
|
||||
int retries = 1,
|
||||
}) async {
|
||||
assert(project != null);
|
||||
@ -355,8 +362,29 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
if (target != null) {
|
||||
command.add('-Ptarget=$target');
|
||||
}
|
||||
if (project.manifest.deferredComponents != null) {
|
||||
if (deferredComponentsEnabled) {
|
||||
command.add('-Pdeferred-components=true');
|
||||
androidBuildInfo.buildInfo.dartDefines.add('validate-deferred-components=$validateDeferredComponents');
|
||||
}
|
||||
// Pass in deferred components regardless of building split aot to satisfy
|
||||
// android dynamic features registry in build.gradle.
|
||||
final List<String> componentNames = <String>[];
|
||||
for (final DeferredComponent component in project.manifest.deferredComponents) {
|
||||
componentNames.add(component.name);
|
||||
}
|
||||
if (componentNames.isNotEmpty) {
|
||||
command.add('-Pdeferred-component-names=${componentNames.join(',')}');
|
||||
// Multi-apk applications cannot use shrinking. This is only relevant when using
|
||||
// android dynamic feature modules.
|
||||
_logger.printStatus(
|
||||
'Shrinking has been disabled for this build due to deferred components. Shrinking is '
|
||||
'not available for multi-apk applications. This limitation is expected to be removed '
|
||||
'when Gradle plugin 4.2+ is available in Flutter.', color: TerminalColor.yellow);
|
||||
command.add('-Pshrink=false');
|
||||
}
|
||||
}
|
||||
command.addAll(androidBuildInfo.buildInfo.toGradleConfig());
|
||||
|
||||
if (buildInfo.fileSystemRoots != null && buildInfo.fileSystemRoots.isNotEmpty) {
|
||||
command.add('-Pfilesystem-roots=${buildInfo.fileSystemRoots.join('|')}');
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
|
||||
import 'base/context.dart';
|
||||
import 'base/deferred_component.dart';
|
||||
import 'base/file_system.dart';
|
||||
import 'base/logger.dart';
|
||||
import 'base/platform.dart';
|
||||
@ -53,7 +54,8 @@ abstract class AssetBundleFactory {
|
||||
@required Logger logger,
|
||||
@required FileSystem fileSystem,
|
||||
@required Platform platform,
|
||||
}) => _ManifestAssetBundleFactory(logger: logger, fileSystem: fileSystem, platform: platform);
|
||||
bool splitDeferredAssets = false,
|
||||
}) => _ManifestAssetBundleFactory(logger: logger, fileSystem: fileSystem, platform: platform, splitDeferredAssets: splitDeferredAssets);
|
||||
|
||||
/// Creates a new [AssetBundle].
|
||||
AssetBundle createBundle();
|
||||
@ -62,6 +64,10 @@ abstract class AssetBundleFactory {
|
||||
abstract class AssetBundle {
|
||||
Map<String, DevFSContent> get entries;
|
||||
|
||||
/// The files that were specified under the deferred components assets sections
|
||||
/// in pubspec.
|
||||
Map<String, Map<String, DevFSContent>> get deferredComponentsEntries;
|
||||
|
||||
/// Additional files that this bundle depends on that are not included in the
|
||||
/// output result.
|
||||
List<File> get additionalDependencies;
|
||||
@ -75,6 +81,7 @@ abstract class AssetBundle {
|
||||
String manifestPath = defaultManifestPath,
|
||||
String assetDirPath,
|
||||
@required String packagesPath,
|
||||
bool deferredComponentsEnabled = false,
|
||||
});
|
||||
}
|
||||
|
||||
@ -83,16 +90,19 @@ class _ManifestAssetBundleFactory implements AssetBundleFactory {
|
||||
@required Logger logger,
|
||||
@required FileSystem fileSystem,
|
||||
@required Platform platform,
|
||||
bool splitDeferredAssets = false,
|
||||
}) : _logger = logger,
|
||||
_fileSystem = fileSystem,
|
||||
_platform = platform;
|
||||
_platform = platform,
|
||||
_splitDeferredAssets = splitDeferredAssets;
|
||||
|
||||
final Logger _logger;
|
||||
final FileSystem _fileSystem;
|
||||
final Platform _platform;
|
||||
final bool _splitDeferredAssets;
|
||||
|
||||
@override
|
||||
AssetBundle createBundle() => ManifestAssetBundle(logger: _logger, fileSystem: _fileSystem, platform: _platform);
|
||||
AssetBundle createBundle() => ManifestAssetBundle(logger: _logger, fileSystem: _fileSystem, platform: _platform, splitDeferredAssets: _splitDeferredAssets);
|
||||
}
|
||||
|
||||
/// An asset bundle based on a pubspec.yaml file.
|
||||
@ -103,19 +113,25 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
@required Logger logger,
|
||||
@required FileSystem fileSystem,
|
||||
@required Platform platform,
|
||||
bool splitDeferredAssets = false,
|
||||
}) : _logger = logger,
|
||||
_fileSystem = fileSystem,
|
||||
_platform = platform,
|
||||
_splitDeferredAssets = splitDeferredAssets,
|
||||
_licenseCollector = LicenseCollector(fileSystem: fileSystem);
|
||||
|
||||
final Logger _logger;
|
||||
final FileSystem _fileSystem;
|
||||
final LicenseCollector _licenseCollector;
|
||||
final Platform _platform;
|
||||
final bool _splitDeferredAssets;
|
||||
|
||||
@override
|
||||
final Map<String, DevFSContent> entries = <String, DevFSContent>{};
|
||||
|
||||
@override
|
||||
final Map<String, Map<String, DevFSContent>> deferredComponentsEntries = <String, Map<String, DevFSContent>>{};
|
||||
|
||||
// If an asset corresponds to a wildcard directory, then it may have been
|
||||
// updated without changes to the manifest. These are only tracked for
|
||||
// the current project.
|
||||
@ -163,6 +179,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
String manifestPath = defaultManifestPath,
|
||||
String assetDirPath,
|
||||
@required String packagesPath,
|
||||
bool deferredComponentsEnabled = false,
|
||||
}) async {
|
||||
assetDirPath ??= getAssetBuildDirectory();
|
||||
FlutterProject flutterProject;
|
||||
@ -197,29 +214,49 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
// in the pubspec.yaml file's assets and font and sections. The
|
||||
// value of each image asset is a list of resolution-specific "variants",
|
||||
// see _AssetDirectoryCache.
|
||||
final List<String> excludeDirs = <String>[
|
||||
assetDirPath,
|
||||
getBuildDirectory(),
|
||||
if (flutterProject.ios.existsSync())
|
||||
flutterProject.ios.hostAppRoot.path,
|
||||
if (flutterProject.macos.existsSync())
|
||||
flutterProject.macos.managedDirectory.path,
|
||||
if (flutterProject.windows.existsSync())
|
||||
flutterProject.windows.managedDirectory.path,
|
||||
if (flutterProject.linux.existsSync())
|
||||
flutterProject.linux.managedDirectory.path,
|
||||
];
|
||||
final Map<_Asset, List<_Asset>> assetVariants = _parseAssets(
|
||||
packageConfig,
|
||||
flutterManifest,
|
||||
wildcardDirectories,
|
||||
assetBasePath,
|
||||
excludeDirs: <String>[
|
||||
assetDirPath,
|
||||
getBuildDirectory(),
|
||||
if (flutterProject.ios.existsSync())
|
||||
flutterProject.ios.hostAppRoot.path,
|
||||
if (flutterProject.macos.existsSync())
|
||||
flutterProject.macos.managedDirectory.path,
|
||||
if (flutterProject.windows.existsSync())
|
||||
flutterProject.windows.managedDirectory.path,
|
||||
if (flutterProject.linux.existsSync())
|
||||
flutterProject.linux.managedDirectory.path,
|
||||
],
|
||||
excludeDirs: excludeDirs,
|
||||
);
|
||||
|
||||
if (assetVariants == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse assets for deferred components.
|
||||
final Map<String, Map<_Asset, List<_Asset>>> deferredComponentsAssetVariants = _parseDeferredComponentsAssets(
|
||||
flutterManifest,
|
||||
packageConfig,
|
||||
assetBasePath,
|
||||
wildcardDirectories,
|
||||
flutterProject.directory,
|
||||
excludeDirs: excludeDirs,
|
||||
);
|
||||
if (!_splitDeferredAssets || !deferredComponentsEnabled) {
|
||||
// Include the assets in the regular set of assets if not using deferred
|
||||
// components.
|
||||
for (final String componentName in deferredComponentsAssetVariants.keys) {
|
||||
assetVariants.addAll(deferredComponentsAssetVariants[componentName]);
|
||||
}
|
||||
deferredComponentsAssetVariants.clear();
|
||||
deferredComponentsEntries.clear();
|
||||
}
|
||||
|
||||
final bool includesMaterialFonts = flutterManifest.usesMaterialDesign;
|
||||
final List<Map<String, dynamic>> fonts = _parseFonts(
|
||||
flutterManifest,
|
||||
@ -314,6 +351,39 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
entries[variant.entryUri.path] ??= DevFSFileContent(variantFile);
|
||||
}
|
||||
}
|
||||
// Save the contents of each deferred component image, image variant, and font
|
||||
// asset in deferredComponentsEntries.
|
||||
if (deferredComponentsAssetVariants != null) {
|
||||
for (final String componentName in deferredComponentsAssetVariants.keys) {
|
||||
deferredComponentsEntries[componentName] = <String, DevFSContent>{};
|
||||
for (final _Asset asset in deferredComponentsAssetVariants[componentName].keys) {
|
||||
final File assetFile = asset.lookupAssetFile(_fileSystem);
|
||||
if (!assetFile.existsSync() && deferredComponentsAssetVariants[componentName][asset].isEmpty) {
|
||||
_logger.printStatus('Error detected in pubspec.yaml:', emphasis: true);
|
||||
_logger.printError('No file or variants found for $asset.\n');
|
||||
if (asset.package != null) {
|
||||
_logger.printError('This asset was included from package ${asset.package.name}.');
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// The file name for an asset's "main" entry is whatever appears in
|
||||
// the pubspec.yaml file. The main entry's file must always exist for
|
||||
// font assets. It need not exist for an image if resolution-specific
|
||||
// variant files exist. An image's main entry is treated the same as a
|
||||
// "1x" resolution variant and if both exist then the explicit 1x
|
||||
// variant is preferred.
|
||||
if (assetFile.existsSync()) {
|
||||
assert(!deferredComponentsAssetVariants[componentName][asset].contains(asset));
|
||||
deferredComponentsAssetVariants[componentName][asset].insert(0, asset);
|
||||
}
|
||||
for (final _Asset variant in deferredComponentsAssetVariants[componentName][asset]) {
|
||||
final File variantFile = variant.lookupAssetFile(_fileSystem);
|
||||
assert(variantFile.existsSync());
|
||||
deferredComponentsEntries[componentName][variant.entryUri.path] ??= DevFSFileContent(variantFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final List<_Asset> materialAssets = <_Asset>[
|
||||
if (flutterManifest.usesMaterialDesign)
|
||||
..._getMaterialAssets(),
|
||||
@ -410,6 +480,50 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
];
|
||||
}
|
||||
|
||||
Map<String, Map<_Asset, List<_Asset>>> _parseDeferredComponentsAssets(
|
||||
FlutterManifest flutterManifest,
|
||||
PackageConfig packageConfig,
|
||||
String assetBasePath,
|
||||
List<Uri> wildcardDirectories,
|
||||
Directory projectDirectory, {
|
||||
List<String> excludeDirs = const <String>[],
|
||||
}) {
|
||||
final List<DeferredComponent> components = flutterManifest.deferredComponents;
|
||||
final Map<String, Map<_Asset, List<_Asset>>> deferredComponentsAssetVariants = <String, Map<_Asset, List<_Asset>>>{};
|
||||
if (components == null) {
|
||||
return deferredComponentsAssetVariants;
|
||||
}
|
||||
for (final DeferredComponent component in components) {
|
||||
deferredComponentsAssetVariants[component.name] = <_Asset, List<_Asset>>{};
|
||||
final _AssetDirectoryCache cache = _AssetDirectoryCache(<String>[], _fileSystem);
|
||||
for (final Uri assetUri in component.assets) {
|
||||
if (assetUri.path.endsWith('/')) {
|
||||
wildcardDirectories.add(assetUri);
|
||||
_parseAssetsFromFolder(
|
||||
packageConfig,
|
||||
flutterManifest,
|
||||
assetBasePath,
|
||||
cache,
|
||||
deferredComponentsAssetVariants[component.name],
|
||||
assetUri,
|
||||
excludeDirs: excludeDirs,
|
||||
);
|
||||
} else {
|
||||
_parseAssetFromFile(
|
||||
packageConfig,
|
||||
flutterManifest,
|
||||
assetBasePath,
|
||||
cache,
|
||||
deferredComponentsAssetVariants[component.name],
|
||||
assetUri,
|
||||
excludeDirs: excludeDirs,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return deferredComponentsAssetVariants;
|
||||
}
|
||||
|
||||
DevFSStringContent _createAssetManifest(Map<_Asset, List<_Asset>> assetVariants) {
|
||||
final Map<String, List<String>> jsonObject = <String, List<String>>{};
|
||||
final List<_Asset> assets = assetVariants.keys.toList()
|
||||
|
@ -167,7 +167,7 @@ class AnsiTerminal implements Terminal {
|
||||
static const String cyan = '\u001b[36m';
|
||||
static const String magenta = '\u001b[35m';
|
||||
static const String yellow = '\u001b[33m';
|
||||
static const String grey = '\u001b[1;30m';
|
||||
static const String grey = '\u001b[90m';
|
||||
|
||||
static const Map<TerminalColor, String> _colorMap = <TerminalColor, String>{
|
||||
TerminalColor.red: red,
|
||||
|
@ -25,10 +25,12 @@ class DepfileService {
|
||||
|
||||
/// Given an [depfile] File, write the depfile contents.
|
||||
///
|
||||
/// If either [inputs] or [outputs] is empty, ensures the file does not
|
||||
/// exist.
|
||||
void writeToFile(Depfile depfile, File output) {
|
||||
if (depfile.inputs.isEmpty || depfile.outputs.isEmpty) {
|
||||
/// If both [inputs] and [outputs] are empty, ensures the file does not
|
||||
/// exist. This can be overriden with the [writeEmpty] parameter when
|
||||
/// both static and runtime dependencies exist and it is not desired
|
||||
/// to force a rerun due to no depfile.
|
||||
void writeToFile(Depfile depfile, File output, {bool writeEmpty = false}) {
|
||||
if (depfile.inputs.isEmpty && depfile.outputs.isEmpty && !writeEmpty) {
|
||||
ErrorHandlingFileSystem.deleteIfExists(output);
|
||||
return;
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ class SourceVisitor implements ResolvedFiles {
|
||||
throw InvalidPatternException(pattern);
|
||||
}
|
||||
if (!environment.fileSystem.directory(filePath).existsSync()) {
|
||||
throw Exception('$filePath does not exist!');
|
||||
environment.fileSystem.directory(filePath).createSync(recursive: true);
|
||||
}
|
||||
for (final FileSystemEntity entity in environment.fileSystem.directory(filePath).listSync()) {
|
||||
final String filename = environment.fileSystem.path.basename(entity.path);
|
||||
|
@ -10,6 +10,7 @@ import '../../base/deferred_component.dart';
|
||||
import '../../base/file_system.dart';
|
||||
import '../../build_info.dart';
|
||||
import '../../globals.dart' as globals hide fs, artifacts, logger, processManager;
|
||||
import '../../project.dart';
|
||||
import '../build_system.dart';
|
||||
import '../depfile.dart';
|
||||
import '../exceptions.dart';
|
||||
@ -40,7 +41,6 @@ abstract class AndroidAssetBundle extends Target {
|
||||
'flutter_assets.d',
|
||||
];
|
||||
|
||||
|
||||
@override
|
||||
Future<void> build(Environment environment) async {
|
||||
if (environment.defines[kBuildMode] == null) {
|
||||
@ -66,6 +66,7 @@ abstract class AndroidAssetBundle extends Target {
|
||||
environment,
|
||||
outputDirectory,
|
||||
targetPlatform: TargetPlatform.android,
|
||||
buildMode: buildMode,
|
||||
);
|
||||
final DepfileService depfileService = DepfileService(
|
||||
fileSystem: environment.fileSystem,
|
||||
@ -173,7 +174,7 @@ class AndroidAot extends AotElfBase {
|
||||
|
||||
/// The selected build mode.
|
||||
///
|
||||
/// This is restricted to [BuildMode.profile] or [BuildMode.release].
|
||||
/// Build mode is restricted to [BuildMode.profile] or [BuildMode.release] for AOT builds.
|
||||
final BuildMode buildMode;
|
||||
|
||||
@override
|
||||
@ -193,6 +194,11 @@ class AndroidAot extends AotElfBase {
|
||||
Source.pattern('{BUILD_DIR}/$_androidAbiName/app.so'),
|
||||
];
|
||||
|
||||
@override
|
||||
List<String> get depfiles => <String>[
|
||||
'flutter_$name.d',
|
||||
];
|
||||
|
||||
@override
|
||||
List<Target> get dependencies => const <Target>[
|
||||
KernelSnapshot(),
|
||||
@ -217,6 +223,12 @@ class AndroidAot extends AotElfBase {
|
||||
output.createSync(recursive: true);
|
||||
}
|
||||
final List<String> extraGenSnapshotOptions = decodeCommaSeparated(environment.defines, kExtraGenSnapshotOptions);
|
||||
final List<File> outputs = <File>[]; // outputs for the depfile
|
||||
final String manifestPath = '${output.path}${globals.platform.pathSeparator}manifest.json';
|
||||
if (environment.defines[kDeferredComponents] == 'true') {
|
||||
extraGenSnapshotOptions.add('--loading_unit_manifest=$manifestPath');
|
||||
outputs.add(environment.fileSystem.file(manifestPath));
|
||||
}
|
||||
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
||||
final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';
|
||||
final String codeSizeDirectory = environment.defines[kCodeSizeDirectory];
|
||||
@ -245,6 +257,22 @@ class AndroidAot extends AotElfBase {
|
||||
if (snapshotExitCode != 0) {
|
||||
throw Exception('AOT snapshotter exited with code $snapshotExitCode');
|
||||
}
|
||||
if (environment.defines[kDeferredComponents] == 'true') {
|
||||
// Parse the manifest for .so paths
|
||||
final List<LoadingUnit> loadingUnits = LoadingUnit.parseLoadingUnitManifest(environment.fileSystem.file(manifestPath), environment.logger);
|
||||
for (final LoadingUnit unit in loadingUnits) {
|
||||
outputs.add(environment.fileSystem.file(unit.path));
|
||||
}
|
||||
}
|
||||
final DepfileService depfileService = DepfileService(
|
||||
fileSystem: environment.fileSystem,
|
||||
logger: environment.logger,
|
||||
);
|
||||
depfileService.writeToFile(
|
||||
Depfile(<File>[], outputs),
|
||||
environment.buildDir.childFile('flutter_$name.d'),
|
||||
writeEmpty: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +284,7 @@ const AndroidAot androidArmRelease = AndroidAot(TargetPlatform.android_arm, Bui
|
||||
const AndroidAot androidArm64Release = AndroidAot(TargetPlatform.android_arm64, BuildMode.release);
|
||||
const AndroidAot androidx64Release = AndroidAot(TargetPlatform.android_x64, BuildMode.release);
|
||||
|
||||
/// A rule paired with [AndroidAot] that copies the produced so files into the output directory.
|
||||
/// A rule paired with [AndroidAot] that copies the produced so file and manifest.json (if present) into the output directory.
|
||||
class AndroidAotBundle extends Target {
|
||||
/// Create an [AndroidAotBundle] implementation for a given [targetPlatform] and [buildMode].
|
||||
const AndroidAotBundle(this.dependency);
|
||||
@ -274,9 +302,16 @@ class AndroidAotBundle extends Target {
|
||||
String get name => 'android_aot_bundle_${getNameForBuildMode(dependency.buildMode)}_'
|
||||
'${getNameForTargetPlatform(dependency.targetPlatform)}';
|
||||
|
||||
TargetPlatform get targetPlatform => dependency.targetPlatform;
|
||||
|
||||
/// The selected build mode.
|
||||
///
|
||||
/// This is restricted to [BuildMode.profile] or [BuildMode.release].
|
||||
BuildMode get buildMode => dependency.buildMode;
|
||||
|
||||
@override
|
||||
List<Source> get inputs => <Source>[
|
||||
Source.pattern('{BUILD_DIR}/$_androidAbiName/app.so'),
|
||||
Source.pattern('{BUILD_DIR}/$_androidAbiName/app.so'),
|
||||
];
|
||||
|
||||
// flutter.gradle has been updated to correctly consume it.
|
||||
@ -285,6 +320,11 @@ class AndroidAotBundle extends Target {
|
||||
Source.pattern('{OUTPUT_DIR}/$_androidAbiName/app.so'),
|
||||
];
|
||||
|
||||
@override
|
||||
List<String> get depfiles => <String>[
|
||||
'flutter_$name.d',
|
||||
];
|
||||
|
||||
@override
|
||||
List<Target> get dependencies => <Target>[
|
||||
dependency,
|
||||
@ -293,25 +333,134 @@ class AndroidAotBundle extends Target {
|
||||
|
||||
@override
|
||||
Future<void> build(Environment environment) async {
|
||||
final File outputFile = environment.buildDir
|
||||
.childDirectory(_androidAbiName)
|
||||
.childFile('app.so');
|
||||
final Directory buildDir = environment.buildDir.childDirectory(_androidAbiName);
|
||||
final Directory outputDirectory = environment.outputDir
|
||||
.childDirectory(_androidAbiName);
|
||||
if (!outputDirectory.existsSync()) {
|
||||
outputDirectory.createSync(recursive: true);
|
||||
}
|
||||
outputFile.copySync(outputDirectory.childFile('app.so').path);
|
||||
final File outputLibFile = buildDir.childFile('app.so');
|
||||
outputLibFile.copySync(outputDirectory.childFile('app.so').path);
|
||||
|
||||
final List<File> inputs = <File>[];
|
||||
final List<File> outputs = <File>[];
|
||||
final File manifestFile = buildDir.childFile('manifest.json');
|
||||
if (manifestFile.existsSync()) {
|
||||
final File destinationFile = outputDirectory.childFile('manifest.json');
|
||||
manifestFile.copySync(destinationFile.path);
|
||||
inputs.add(manifestFile);
|
||||
outputs.add(destinationFile);
|
||||
}
|
||||
final DepfileService depfileService = DepfileService(
|
||||
fileSystem: environment.fileSystem,
|
||||
logger: environment.logger,
|
||||
);
|
||||
depfileService.writeToFile(
|
||||
Depfile(inputs, outputs),
|
||||
environment.buildDir.childFile('flutter_$name.d'),
|
||||
writeEmpty: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// AndroidBundleAot instances.
|
||||
const Target androidArmProfileBundle = AndroidAotBundle(androidArmProfile);
|
||||
const Target androidArm64ProfileBundle = AndroidAotBundle(androidArm64Profile);
|
||||
const Target androidx64ProfileBundle = AndroidAotBundle(androidx64Profile);
|
||||
const Target androidArmReleaseBundle = AndroidAotBundle(androidArmRelease);
|
||||
const Target androidArm64ReleaseBundle = AndroidAotBundle(androidArm64Release);
|
||||
const Target androidx64ReleaseBundle = AndroidAotBundle(androidx64Release);
|
||||
const AndroidAotBundle androidArmProfileBundle = AndroidAotBundle(androidArmProfile);
|
||||
const AndroidAotBundle androidArm64ProfileBundle = AndroidAotBundle(androidArm64Profile);
|
||||
const AndroidAotBundle androidx64ProfileBundle = AndroidAotBundle(androidx64Profile);
|
||||
const AndroidAotBundle androidArmReleaseBundle = AndroidAotBundle(androidArmRelease);
|
||||
const AndroidAotBundle androidArm64ReleaseBundle = AndroidAotBundle(androidArm64Release);
|
||||
const AndroidAotBundle androidx64ReleaseBundle = AndroidAotBundle(androidx64Release);
|
||||
|
||||
// Rule that copies split aot library files to the intermediate dirs of each deferred component.
|
||||
class AndroidAotDeferredComponentsBundle extends Target {
|
||||
/// Create an [AndroidAotDeferredComponentsBundle] implementation for a given [targetPlatform] and [buildMode].
|
||||
///
|
||||
/// If [components] is not provided, it will be read from the pubspec.yaml manifest.
|
||||
AndroidAotDeferredComponentsBundle(this.dependency, {List<DeferredComponent> components}) : _components = components;
|
||||
|
||||
/// The [AndroidAotBundle] instance this bundle rule depends on.
|
||||
final AndroidAotBundle dependency;
|
||||
|
||||
List<DeferredComponent> _components;
|
||||
|
||||
/// The name of the produced Android ABI.
|
||||
String get _androidAbiName {
|
||||
return getNameForAndroidArch(
|
||||
getAndroidArchForName(getNameForTargetPlatform(dependency.targetPlatform)));
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => 'android_aot_deferred_components_bundle_${getNameForBuildMode(dependency.buildMode)}_'
|
||||
'${getNameForTargetPlatform(dependency.targetPlatform)}';
|
||||
|
||||
TargetPlatform get targetPlatform => dependency.targetPlatform;
|
||||
|
||||
@override
|
||||
List<Source> get inputs => <Source>[
|
||||
// Tracking app.so is enough to invalidate the dynamically named
|
||||
// loading unit libs as changes to loading units guarantee
|
||||
// changes to app.so as well. This task does not actually
|
||||
// copy app.so.
|
||||
Source.pattern('{OUTPUT_DIR}/$_androidAbiName/app.so'),
|
||||
const Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
|
||||
];
|
||||
|
||||
@override
|
||||
List<Source> get outputs => const <Source>[];
|
||||
|
||||
@override
|
||||
List<String> get depfiles => <String>[
|
||||
'flutter_$name.d',
|
||||
];
|
||||
|
||||
@override
|
||||
List<Target> get dependencies => <Target>[
|
||||
dependency,
|
||||
];
|
||||
|
||||
@override
|
||||
Future<void> build(Environment environment) async {
|
||||
_components ??= FlutterProject.current().manifest.deferredComponents ?? <DeferredComponent>[];
|
||||
final List<String> abis = <String>[_androidAbiName];
|
||||
final List<LoadingUnit> generatedLoadingUnits = LoadingUnit.parseGeneratedLoadingUnits(environment.outputDir, environment.logger, abis: abis);
|
||||
for (final DeferredComponent component in _components) {
|
||||
component.assignLoadingUnits(generatedLoadingUnits);
|
||||
}
|
||||
final Depfile libDepfile = copyDeferredComponentSoFiles(environment, _components, generatedLoadingUnits, environment.projectDir.childDirectory('build'), abis, dependency.buildMode);
|
||||
|
||||
final File manifestFile = environment.outputDir.childDirectory(_androidAbiName).childFile('manifest.json');
|
||||
if (manifestFile.existsSync()) {
|
||||
libDepfile.inputs.add(manifestFile);
|
||||
}
|
||||
|
||||
final DepfileService depfileService = DepfileService(
|
||||
fileSystem: environment.fileSystem,
|
||||
logger: environment.logger,
|
||||
);
|
||||
depfileService.writeToFile(
|
||||
libDepfile,
|
||||
environment.buildDir.childFile('flutter_$name.d'),
|
||||
writeEmpty: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Target androidArmProfileDeferredComponentsBundle = AndroidAotDeferredComponentsBundle(androidArmProfileBundle);
|
||||
Target androidArm64ProfileDeferredComponentsBundle = AndroidAotDeferredComponentsBundle(androidArm64ProfileBundle);
|
||||
Target androidx64ProfileDeferredComponentsBundle = AndroidAotDeferredComponentsBundle(androidx64ProfileBundle);
|
||||
Target androidArmReleaseDeferredComponentsBundle = AndroidAotDeferredComponentsBundle(androidArmReleaseBundle);
|
||||
Target androidArm64ReleaseDeferredComponentsBundle = AndroidAotDeferredComponentsBundle(androidArm64ReleaseBundle);
|
||||
Target androidx64ReleaseDeferredComponentsBundle = AndroidAotDeferredComponentsBundle(androidx64ReleaseBundle);
|
||||
|
||||
/// A set of all target names that build deferred component apps.
|
||||
Set<String> deferredComponentsTargets = <String>{
|
||||
androidArmProfileDeferredComponentsBundle.name,
|
||||
androidArm64ProfileDeferredComponentsBundle.name,
|
||||
androidx64ProfileDeferredComponentsBundle.name,
|
||||
androidArmReleaseDeferredComponentsBundle.name,
|
||||
androidArm64ReleaseDeferredComponentsBundle.name,
|
||||
androidx64ReleaseDeferredComponentsBundle.name,
|
||||
};
|
||||
|
||||
/// Utility method to copy and rename the required .so shared libs from the build output
|
||||
/// to the correct component intermediate directory.
|
||||
|
@ -34,6 +34,7 @@ const String kBundleSkSLPath = 'BundleSkSLPath';
|
||||
Future<Depfile> copyAssets(Environment environment, Directory outputDirectory, {
|
||||
Map<String, DevFSContent> additionalContent,
|
||||
@required TargetPlatform targetPlatform,
|
||||
BuildMode buildMode,
|
||||
}) async {
|
||||
// Check for an SkSL bundle.
|
||||
final String shaderBundlePath = environment.inputs[kBundleSkSLPath];
|
||||
@ -51,11 +52,13 @@ Future<Depfile> copyAssets(Environment environment, Directory outputDirectory, {
|
||||
logger: environment.logger,
|
||||
fileSystem: environment.fileSystem,
|
||||
platform: globals.platform,
|
||||
splitDeferredAssets: buildMode != BuildMode.debug && buildMode != BuildMode.jitRelease,
|
||||
).createBundle();
|
||||
final int resultCode = await assetBundle.build(
|
||||
manifestPath: pubspecFile.path,
|
||||
packagesPath: environment.projectDir.childFile('.packages').path,
|
||||
assetDirPath: null,
|
||||
deferredComponentsEnabled: environment.defines[kDeferredComponents] == 'true',
|
||||
);
|
||||
if (resultCode != 0) {
|
||||
throw Exception('Failed to bundle asset files.');
|
||||
@ -114,6 +117,56 @@ Future<Depfile> copyAssets(Environment environment, Directory outputDirectory, {
|
||||
resource.release();
|
||||
}
|
||||
}));
|
||||
|
||||
// Copy deferred components assets only for release or profile builds.
|
||||
// The assets are included in assetBundle.entries as a normal asset when
|
||||
// building as debug.
|
||||
if (environment.defines[kDeferredComponents] == 'true') {
|
||||
await Future.wait<void>(
|
||||
assetBundle.deferredComponentsEntries.entries.map<Future<void>>((MapEntry<String, Map<String, DevFSContent>> componentEntries) async {
|
||||
final Directory componentOutputDir =
|
||||
environment.projectDir
|
||||
.childDirectory('build')
|
||||
.childDirectory(componentEntries.key)
|
||||
.childDirectory('intermediates')
|
||||
.childDirectory('flutter');
|
||||
await Future.wait<void>(
|
||||
componentEntries.value.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
|
||||
final PoolResource resource = await pool.request();
|
||||
try {
|
||||
// This will result in strange looking files, for example files with `/`
|
||||
// on Windows or files that end up getting URI encoded such as `#.ext`
|
||||
// to `%23.ext`. However, we have to keep it this way since the
|
||||
// platform channels in the framework will URI encode these values,
|
||||
// and the native APIs will look for files this way.
|
||||
|
||||
// If deferred components are disabled, then copy assets to regular location.
|
||||
final File file = environment.defines[kDeferredComponents] == 'true'
|
||||
? environment.fileSystem.file(
|
||||
environment.fileSystem.path.join(componentOutputDir.path, buildMode.name, 'deferred_assets', 'flutter_assets', entry.key))
|
||||
: environment.fileSystem.file(
|
||||
environment.fileSystem.path.join(outputDirectory.path, entry.key));
|
||||
outputs.add(file);
|
||||
file.parent.createSync(recursive: true);
|
||||
final DevFSContent content = entry.value;
|
||||
if (content is DevFSFileContent && content.file is File) {
|
||||
inputs.add(content.file as File);
|
||||
if (!await iconTreeShaker.subsetFont(
|
||||
input: content.file as File,
|
||||
outputPath: file.path,
|
||||
relativePath: entry.key,
|
||||
)) {
|
||||
await (content.file as File).copy(file.path);
|
||||
}
|
||||
} else {
|
||||
await file.writeAsBytes(await entry.value.contentsAsBytes());
|
||||
}
|
||||
} finally {
|
||||
resource.release();
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
final Depfile depfile = Depfile(inputs + assetBundle.additionalDependencies, outputs);
|
||||
if (shaderBundlePath != null) {
|
||||
final File skSLBundleFile = environment.fileSystem
|
||||
|
@ -45,9 +45,9 @@ const String kExtraFrontEndOptions = 'ExtraFrontEndOptions';
|
||||
/// This is expected to be a comma separated list of strings.
|
||||
const String kExtraGenSnapshotOptions = 'ExtraGenSnapshotOptions';
|
||||
|
||||
/// Whether the app should run gen_snapshot as a split aot build for deferred
|
||||
/// Whether the build should run gen_snapshot as a split aot build for deferred
|
||||
/// components.
|
||||
const String kSplitAot = 'SplitAot';
|
||||
const String kDeferredComponents = 'DeferredComponents';
|
||||
|
||||
/// Whether to strip source code information out of release builds and where to save it.
|
||||
const String kSplitDebugInfo = 'SplitDebugInfo';
|
||||
@ -131,6 +131,7 @@ class CopyFlutterBundle extends Target {
|
||||
environment,
|
||||
environment.outputDir,
|
||||
targetPlatform: TargetPlatform.android,
|
||||
buildMode: buildMode,
|
||||
);
|
||||
final DepfileService depfileService = DepfileService(
|
||||
fileSystem: environment.fileSystem,
|
||||
|
@ -8,25 +8,26 @@ import 'package:meta/meta.dart';
|
||||
|
||||
import '../../android/deferred_components_gen_snapshot_validator.dart';
|
||||
import '../../base/deferred_component.dart';
|
||||
import '../../build_info.dart';
|
||||
import '../../project.dart';
|
||||
import '../build_system.dart';
|
||||
import '../depfile.dart';
|
||||
import 'android.dart';
|
||||
|
||||
/// Creates a [DeferredComponentsGenSnapshotValidator], runs the checks, and displays the validator
|
||||
/// output to the developer if changes are recommended.
|
||||
class DeferredComponentsGenSnapshotValidatorTarget extends Target {
|
||||
/// Create an [AndroidAotDeferredComponentsBundle] implementation for a given [targetPlatform] and [buildMode].
|
||||
DeferredComponentsGenSnapshotValidatorTarget({
|
||||
@required this.dependency,
|
||||
@required this.abis,
|
||||
@required this.deferredComponentsDependencies,
|
||||
@required this.nonDeferredComponentsDependencies,
|
||||
this.title,
|
||||
this.exitOnFail = true,
|
||||
String name = 'deferred_components_setup_validator',
|
||||
}) : _name = name;
|
||||
});
|
||||
|
||||
/// The [AndroidAotDeferredComponentsBundle] derived target instances this rule depends on packed
|
||||
/// as a [CompositeTarget].
|
||||
final CompositeTarget dependency;
|
||||
/// The [AndroidAotDeferredComponentsBundle] derived target instances this rule depends on.
|
||||
final List<AndroidAotDeferredComponentsBundle> deferredComponentsDependencies;
|
||||
final List<Target> nonDeferredComponentsDependencies;
|
||||
|
||||
/// The title of the [DeferredComponentsGenSnapshotValidator] that is
|
||||
/// displayed to the developer when logging results.
|
||||
@ -37,11 +38,20 @@ class DeferredComponentsGenSnapshotValidatorTarget extends Target {
|
||||
final bool exitOnFail;
|
||||
|
||||
/// The abis to validate.
|
||||
final List<String> abis;
|
||||
List<String> get _abis {
|
||||
final List<String> abis = <String>[];
|
||||
for (final AndroidAotDeferredComponentsBundle target in deferredComponentsDependencies) {
|
||||
if (deferredComponentsTargets.contains(target.name)) {
|
||||
abis.add(
|
||||
getNameForAndroidArch(getAndroidArchForName(getNameForTargetPlatform(target.dependency.targetPlatform)))
|
||||
);
|
||||
}
|
||||
}
|
||||
return abis;
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
final String _name;
|
||||
String get name => 'deferred_components_gen_snapshot_validator';
|
||||
|
||||
@override
|
||||
List<Source> get inputs => const <Source>[];
|
||||
@ -55,7 +65,11 @@ class DeferredComponentsGenSnapshotValidatorTarget extends Target {
|
||||
];
|
||||
|
||||
@override
|
||||
List<Target> get dependencies => dependency == null ? <Target>[] : <Target>[dependency];
|
||||
List<Target> get dependencies {
|
||||
final List<Target> deps = <Target>[CompositeTarget(deferredComponentsDependencies)];
|
||||
deps.addAll(nonDeferredComponentsDependencies);
|
||||
return deps;
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
DeferredComponentsGenSnapshotValidator validator;
|
||||
@ -75,7 +89,7 @@ class DeferredComponentsGenSnapshotValidatorTarget extends Target {
|
||||
final List<LoadingUnit> generatedLoadingUnits = LoadingUnit.parseGeneratedLoadingUnits(
|
||||
environment.outputDir,
|
||||
environment.logger,
|
||||
abis: abis
|
||||
abis: _abis
|
||||
);
|
||||
|
||||
validator
|
||||
|
@ -14,6 +14,7 @@ import '../build_system/depfile.dart';
|
||||
import '../build_system/targets/android.dart';
|
||||
import '../build_system/targets/assets.dart';
|
||||
import '../build_system/targets/common.dart';
|
||||
import '../build_system/targets/deferred_components.dart';
|
||||
import '../build_system/targets/ios.dart';
|
||||
import '../build_system/targets/linux.dart';
|
||||
import '../build_system/targets/macos.dart';
|
||||
@ -27,34 +28,34 @@ import '../reporting/reporting.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
|
||||
/// All currently implemented targets.
|
||||
const List<Target> _kDefaultTargets = <Target>[
|
||||
List<Target> _kDefaultTargets = <Target>[
|
||||
// Shared targets
|
||||
CopyAssets(),
|
||||
KernelSnapshot(),
|
||||
AotElfProfile(TargetPlatform.android_arm),
|
||||
AotElfRelease(TargetPlatform.android_arm),
|
||||
AotAssemblyProfile(),
|
||||
AotAssemblyRelease(),
|
||||
const CopyAssets(),
|
||||
const KernelSnapshot(),
|
||||
const AotElfProfile(TargetPlatform.android_arm),
|
||||
const AotElfRelease(TargetPlatform.android_arm),
|
||||
const AotAssemblyProfile(),
|
||||
const AotAssemblyRelease(),
|
||||
// macOS targets
|
||||
DebugMacOSFramework(),
|
||||
DebugMacOSBundleFlutterAssets(),
|
||||
ProfileMacOSBundleFlutterAssets(),
|
||||
ReleaseMacOSBundleFlutterAssets(),
|
||||
const DebugMacOSFramework(),
|
||||
const DebugMacOSBundleFlutterAssets(),
|
||||
const ProfileMacOSBundleFlutterAssets(),
|
||||
const ReleaseMacOSBundleFlutterAssets(),
|
||||
// Linux targets
|
||||
DebugBundleLinuxAssets(TargetPlatform.linux_x64),
|
||||
DebugBundleLinuxAssets(TargetPlatform.linux_arm64),
|
||||
ProfileBundleLinuxAssets(TargetPlatform.linux_x64),
|
||||
ProfileBundleLinuxAssets(TargetPlatform.linux_arm64),
|
||||
ReleaseBundleLinuxAssets(TargetPlatform.linux_x64),
|
||||
ReleaseBundleLinuxAssets(TargetPlatform.linux_arm64),
|
||||
const DebugBundleLinuxAssets(TargetPlatform.linux_x64),
|
||||
const DebugBundleLinuxAssets(TargetPlatform.linux_arm64),
|
||||
const ProfileBundleLinuxAssets(TargetPlatform.linux_x64),
|
||||
const ProfileBundleLinuxAssets(TargetPlatform.linux_arm64),
|
||||
const ReleaseBundleLinuxAssets(TargetPlatform.linux_x64),
|
||||
const ReleaseBundleLinuxAssets(TargetPlatform.linux_arm64),
|
||||
// Web targets
|
||||
WebServiceWorker(),
|
||||
ReleaseAndroidApplication(),
|
||||
const WebServiceWorker(),
|
||||
const ReleaseAndroidApplication(),
|
||||
// This is a one-off rule for bundle and aot compat.
|
||||
CopyFlutterBundle(),
|
||||
const CopyFlutterBundle(),
|
||||
// Android targets,
|
||||
DebugAndroidApplication(),
|
||||
ProfileAndroidApplication(),
|
||||
const DebugAndroidApplication(),
|
||||
const ProfileAndroidApplication(),
|
||||
// Android ABI specific AOT rules.
|
||||
androidArmProfileBundle,
|
||||
androidArm64ProfileBundle,
|
||||
@ -62,15 +63,22 @@ const List<Target> _kDefaultTargets = <Target>[
|
||||
androidArmReleaseBundle,
|
||||
androidArm64ReleaseBundle,
|
||||
androidx64ReleaseBundle,
|
||||
// Deferred component enabled AOT rules
|
||||
androidArmProfileDeferredComponentsBundle,
|
||||
androidArm64ProfileDeferredComponentsBundle,
|
||||
androidx64ProfileDeferredComponentsBundle,
|
||||
androidArmReleaseDeferredComponentsBundle,
|
||||
androidArm64ReleaseDeferredComponentsBundle,
|
||||
androidx64ReleaseDeferredComponentsBundle,
|
||||
// iOS targets
|
||||
DebugIosApplicationBundle(),
|
||||
ProfileIosApplicationBundle(),
|
||||
ReleaseIosApplicationBundle(),
|
||||
const DebugIosApplicationBundle(),
|
||||
const ProfileIosApplicationBundle(),
|
||||
const ReleaseIosApplicationBundle(),
|
||||
// Windows targets
|
||||
UnpackWindows(),
|
||||
DebugBundleWindowsAssets(),
|
||||
ProfileBundleWindowsAssets(),
|
||||
ReleaseBundleWindowsAssets(),
|
||||
const UnpackWindows(),
|
||||
const DebugBundleWindowsAssets(),
|
||||
const ProfileBundleWindowsAssets(),
|
||||
const ReleaseBundleWindowsAssets(),
|
||||
];
|
||||
|
||||
// TODO(ianh): https://github.com/dart-lang/args/issues/181 will allow us to remove useLegacyNames
|
||||
@ -171,6 +179,24 @@ class AssembleCommand extends FlutterCommand {
|
||||
return results;
|
||||
}
|
||||
|
||||
bool isDeferredComponentsTargets() {
|
||||
for (final String targetName in argResults.rest) {
|
||||
if (deferredComponentsTargets.contains(targetName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isDebug() {
|
||||
for (final String targetName in argResults.rest) {
|
||||
if (targetName.contains('debug')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// The environmental configuration for a build invocation.
|
||||
Environment createEnvironment() {
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
@ -221,6 +247,10 @@ class AssembleCommand extends FlutterCommand {
|
||||
if (argResults.wasParsed(useLegacyNames ? kDartDefines : FlutterOptions.kDartDefinesOption)) {
|
||||
results[kDartDefines] = (argResults[useLegacyNames ? kDartDefines : FlutterOptions.kDartDefinesOption] as List<String>).join(',');
|
||||
}
|
||||
results[kDeferredComponents] = 'false';
|
||||
if (FlutterProject.current().manifest.deferredComponents != null && isDeferredComponentsTargets() && !isDebug()) {
|
||||
results[kDeferredComponents] = 'true';
|
||||
}
|
||||
if (argResults.wasParsed(useLegacyNames ? kExtraFrontEndOptions : FlutterOptions.kExtraFrontEndOptions)) {
|
||||
results[kExtraFrontEndOptions] = (argResults[useLegacyNames ? kExtraFrontEndOptions : FlutterOptions.kExtraFrontEndOptions] as List<String>).join(',');
|
||||
}
|
||||
@ -229,11 +259,37 @@ class AssembleCommand extends FlutterCommand {
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
final Environment env = createEnvironment();
|
||||
final List<Target> targets = createTargets();
|
||||
final Target target = targets.length == 1 ? targets.single : CompositeTarget(targets);
|
||||
final List<Target> nonDeferredTargets = <Target>[];
|
||||
final List<Target> deferredTargets = <AndroidAotDeferredComponentsBundle>[];
|
||||
for (final Target target in targets) {
|
||||
if (deferredComponentsTargets.contains(target.name)) {
|
||||
deferredTargets.add(target);
|
||||
} else {
|
||||
nonDeferredTargets.add(target);
|
||||
}
|
||||
}
|
||||
Target target;
|
||||
final List<String> decodedDefines = decodeDartDefines(env.defines, kDartDefines);
|
||||
if (FlutterProject.current().manifest.deferredComponents != null
|
||||
&& decodedDefines.contains('validate-deferred-components=true')
|
||||
&& deferredTargets.isNotEmpty
|
||||
&& !isDebug()) {
|
||||
// Add deferred components validation target that require loading units.
|
||||
target = DeferredComponentsGenSnapshotValidatorTarget(
|
||||
deferredComponentsDependencies: deferredTargets.cast<AndroidAotDeferredComponentsBundle>(),
|
||||
nonDeferredComponentsDependencies: nonDeferredTargets,
|
||||
title: 'Deferred components gen_snapshot validation',
|
||||
);
|
||||
} else if (targets.length > 1) {
|
||||
target = CompositeTarget(targets);
|
||||
} else if (targets.isNotEmpty) {
|
||||
target = targets.single;
|
||||
}
|
||||
final BuildResult result = await _buildSystem.build(
|
||||
target,
|
||||
createEnvironment(),
|
||||
env,
|
||||
buildSystemConfig: BuildSystemConfig(
|
||||
resourcePoolSize: argResults.wasParsed('resource-pool-size')
|
||||
? int.tryParse(stringArg('resource-pool-size'))
|
||||
@ -251,6 +307,7 @@ class AssembleCommand extends FlutterCommand {
|
||||
throwToolExit('');
|
||||
}
|
||||
globals.printTrace('build succeeded.');
|
||||
|
||||
if (argResults.wasParsed('build-inputs')) {
|
||||
writeListIfChanged(result.inputFiles, stringArg('build-inputs'));
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
|
||||
import '../android/android_builder.dart';
|
||||
import '../android/build_validation.dart';
|
||||
import '../android/deferred_components_prebuild_validator.dart';
|
||||
import '../android/gradle_utils.dart';
|
||||
import '../base/deferred_component.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../build_info.dart';
|
||||
import '../cache.dart';
|
||||
import '../globals.dart' as globals;
|
||||
@ -37,11 +40,30 @@ class BuildAppBundleCommand extends BuildSubCommand {
|
||||
usesAnalyzeSizeFlag();
|
||||
addAndroidSpecificBuildOptions(hide: !verboseHelp);
|
||||
argParser.addMultiOption('target-platform',
|
||||
splitCommas: true,
|
||||
defaultsTo: <String>['android-arm', 'android-arm64', 'android-x64'],
|
||||
allowed: <String>['android-arm', 'android-arm64', 'android-x64'],
|
||||
help: 'The target platform for which the app is compiled.',
|
||||
);
|
||||
splitCommas: true,
|
||||
defaultsTo: <String>['android-arm', 'android-arm64', 'android-x64'],
|
||||
allowed: <String>['android-arm', 'android-arm64', 'android-x64'],
|
||||
help: 'The target platform for which the app is compiled.',
|
||||
);
|
||||
argParser.addFlag('deferred-components',
|
||||
negatable: true,
|
||||
defaultsTo: true,
|
||||
help: 'Setting to false disables building with deferred components. All deferred code '
|
||||
'will be compiled into the base app, and assets act as if they were defined under'
|
||||
' the regular assets section in pubspec.yaml. This flag has no effect on '
|
||||
'non-deferred components apps.',
|
||||
);
|
||||
argParser.addFlag('validate-deferred-components',
|
||||
negatable: true,
|
||||
defaultsTo: true,
|
||||
help: 'When enabled, deferred component apps will fail to build if setup problems are '
|
||||
'detected that would prevent deferred components from functioning properly. The '
|
||||
'tooling also provides guidance on how to set up the project files to pass this '
|
||||
'verification. Disabling setup verification will always attempt to fully build '
|
||||
'the app regardless of any problems detected. Builds that are part of CI testing '
|
||||
'and advanced users with custom deferred components implementations should disable '
|
||||
'setup verification. This flag has no effect on non-deferred components apps.',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -84,15 +106,49 @@ class BuildAppBundleCommand extends BuildSubCommand {
|
||||
if (globals.androidSdk == null) {
|
||||
exitWithNoSdkMessage();
|
||||
}
|
||||
|
||||
final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(await getBuildInfo(),
|
||||
targetArchs: stringsArg('target-platform').map<AndroidArch>(getAndroidArchForName),
|
||||
);
|
||||
// Do all setup verification that doesn't involve loading units. Checks that
|
||||
// require generated loading units are done after gen_snapshot in assemble.
|
||||
if (FlutterProject.current().manifest.deferredComponents != null && boolArg('deferred-components') && boolArg('validate-deferred-components') && !boolArg('debug')) {
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
FlutterProject.current().directory,
|
||||
globals.logger,
|
||||
title: 'Deferred components prebuild validation',
|
||||
exitOnFail: true,
|
||||
);
|
||||
validator.clearOutputDir();
|
||||
await validator.checkAndroidDynamicFeature(FlutterProject.current().manifest.deferredComponents);
|
||||
validator.checkAndroidResourcesStrings(FlutterProject.current().manifest.deferredComponents);
|
||||
|
||||
validator.handleResults();
|
||||
|
||||
// Delete intermediates libs dir for components to resolve mismatching
|
||||
// abis supported by base and dynamic feature modules.
|
||||
for (final DeferredComponent component in FlutterProject.current().manifest.deferredComponents) {
|
||||
final Directory deferredLibsIntermediate = FlutterProject.current().directory
|
||||
.childDirectory('build')
|
||||
.childDirectory(component.name)
|
||||
.childDirectory('intermediates')
|
||||
.childDirectory('flutter')
|
||||
.childDirectory(androidBuildInfo.buildInfo.mode.name)
|
||||
.childDirectory('deferred_libs');
|
||||
if (deferredLibsIntermediate.existsSync()) {
|
||||
deferredLibsIntermediate.deleteSync(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateBuild(androidBuildInfo);
|
||||
displayNullSafetyMode(androidBuildInfo.buildInfo);
|
||||
await androidBuilder.buildAab(
|
||||
project: FlutterProject.current(),
|
||||
target: targetFile,
|
||||
androidBuildInfo: androidBuildInfo,
|
||||
validateDeferredComponents: boolArg('validate-deferred-components'),
|
||||
deferredComponentsEnabled: boolArg('deferred-components') && !boolArg('debug'),
|
||||
);
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ class FlutterOptions {
|
||||
static const String kAnalyzeSize = 'analyze-size';
|
||||
static const String kNullAssertions = 'null-assertions';
|
||||
static const String kAndroidGradleDaemon = 'android-gradle-daemon';
|
||||
static const String kDeferredComponents = 'deferred-components';
|
||||
}
|
||||
|
||||
abstract class FlutterCommand extends Command<void> {
|
||||
|
@ -0,0 +1,46 @@
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: "com.android.dynamic-feature"
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
sourceSets {
|
||||
applicationVariants.all { variant ->
|
||||
main.assets.srcDirs += "${project.buildDir}/intermediates/flutter/${variant.name}/deferred_assets"
|
||||
main.jniLibs.srcDirs += "${project.buildDir}/intermediates/flutter/${variant.name}/deferred_libs"
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility 1.8
|
||||
targetCompatibility 1.8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":app")
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:dist="http://schemas.android.com/apk/distribution"
|
||||
package="{{androidIdentifier}}.{{componentName}}">
|
||||
|
||||
<dist:module
|
||||
dist:instant="false"
|
||||
dist:title="@string/{{componentName}}Name">
|
||||
<dist:delivery>
|
||||
<dist:on-demand />
|
||||
</dist:delivery>
|
||||
<dist:fusing dist:include="true" />
|
||||
</dist:module>
|
||||
</manifest>
|
@ -140,6 +140,8 @@
|
||||
"templates/cocoapods/Podfile-ios-objc",
|
||||
"templates/cocoapods/Podfile-ios-swift",
|
||||
"templates/cocoapods/Podfile-macos",
|
||||
"templates/module/android/deferred_component/build.gradle.tmpl",
|
||||
"templates/module/android/deferred_component/src/main/AndroidManifest.xml.tmpl",
|
||||
"templates/module/android/gradle/build.gradle.copy.tmpl",
|
||||
"templates/module/android/gradle/gradle.properties.tmpl",
|
||||
"templates/module/android/host_app_common/app.tmpl/build.gradle.tmpl",
|
||||
|
@ -22,7 +22,7 @@ void main() {
|
||||
Environment env;
|
||||
|
||||
Environment createEnvironment() {
|
||||
final Map<String, String> defines = <String, String>{ kSplitAot: 'true' };
|
||||
final Map<String, String> defines = <String, String>{ kDeferredComponents: 'true' };
|
||||
final Environment result = Environment(
|
||||
outputDir: fileSystem.directory('/output'),
|
||||
buildDir: fileSystem.directory('/build'),
|
||||
|
@ -10,9 +10,6 @@ import 'package:flutter_tools/src/android/deferred_components_validator.dart';
|
||||
import 'package:flutter_tools/src/base/deferred_component.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||
import 'package:flutter_tools/src/build_system/targets/common.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
@ -20,37 +17,20 @@ import '../../src/context.dart';
|
||||
void main() {
|
||||
FileSystem fileSystem;
|
||||
BufferLogger logger;
|
||||
Environment env;
|
||||
|
||||
Environment createEnvironment() {
|
||||
final Map<String, String> defines = <String, String>{ kSplitAot: 'true' };
|
||||
final Environment result = Environment(
|
||||
outputDir: fileSystem.directory('/output'),
|
||||
buildDir: fileSystem.directory('/build'),
|
||||
projectDir: fileSystem.directory('/project'),
|
||||
defines: defines,
|
||||
inputs: <String, String>{},
|
||||
cacheDir: fileSystem.directory('/cache'),
|
||||
flutterRootDir: fileSystem.directory('/flutter_root'),
|
||||
artifacts: globals.artifacts,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
processManager: globals.processManager,
|
||||
engineVersion: 'invalidEngineVersion',
|
||||
generateDartPluginRegistry: false,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
Directory projectDir;
|
||||
Directory flutterRootDir;
|
||||
|
||||
setUp(() {
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
logger = BufferLogger.test();
|
||||
env = createEnvironment();
|
||||
projectDir = fileSystem.directory('/project');
|
||||
flutterRootDir = fileSystem.directory('/flutter_root');
|
||||
});
|
||||
|
||||
testWithoutContext('No checks passes', () async {
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
env,
|
||||
projectDir,
|
||||
logger,
|
||||
exitOnFail: false,
|
||||
title: 'test check',
|
||||
);
|
||||
@ -61,7 +41,8 @@ void main() {
|
||||
|
||||
testWithoutContext('clearTempDir passes', () async {
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
env,
|
||||
projectDir,
|
||||
logger,
|
||||
exitOnFail: false,
|
||||
title: 'test check',
|
||||
);
|
||||
@ -72,7 +53,7 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext('androidComponentSetup build.gradle does not exist', () async {
|
||||
final Directory templatesDir = env.flutterRootDir.childDirectory('templates').childDirectory('deferred_component');
|
||||
final Directory templatesDir = flutterRootDir.childDirectory('templates').childDirectory('deferred_component');
|
||||
final File buildGradleTemplate = templatesDir.childFile('build.gradle.tmpl');
|
||||
final File androidManifestTemplate = templatesDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml.tmpl');
|
||||
if (templatesDir.existsSync()) {
|
||||
@ -84,12 +65,13 @@ void main() {
|
||||
androidManifestTemplate.writeAsStringSync('fake AndroidManigest.xml template {{componentName}}', flush: true, mode: FileMode.append);
|
||||
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
env,
|
||||
projectDir,
|
||||
logger,
|
||||
exitOnFail: false,
|
||||
title: 'test check',
|
||||
templatesDir: templatesDir,
|
||||
);
|
||||
final Directory componentDir = env.projectDir.childDirectory('android').childDirectory('component1');
|
||||
final Directory componentDir = projectDir.childDirectory('android').childDirectory('component1');
|
||||
final File file = componentDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml');
|
||||
if (file.existsSync()) {
|
||||
file.deleteSync();
|
||||
@ -109,7 +91,7 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext('androidComponentSetup AndroidManifest.xml does not exist', () async {
|
||||
final Directory templatesDir = env.flutterRootDir.childDirectory('templates').childDirectory('deferred_component');
|
||||
final Directory templatesDir = flutterRootDir.childDirectory('templates').childDirectory('deferred_component');
|
||||
final File buildGradleTemplate = templatesDir.childFile('build.gradle.tmpl');
|
||||
final File androidManifestTemplate = templatesDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml.tmpl');
|
||||
if (templatesDir.existsSync()) {
|
||||
@ -121,12 +103,13 @@ void main() {
|
||||
androidManifestTemplate.writeAsStringSync('fake AndroidManigest.xml template {{componentName}}', flush: true, mode: FileMode.append);
|
||||
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
env,
|
||||
projectDir,
|
||||
logger,
|
||||
exitOnFail: false,
|
||||
title: 'test check',
|
||||
templatesDir: templatesDir,
|
||||
);
|
||||
final Directory componentDir = env.projectDir.childDirectory('android').childDirectory('component1');
|
||||
final Directory componentDir = projectDir.childDirectory('android').childDirectory('component1');
|
||||
final File file = componentDir.childFile('build.gradle');
|
||||
if (file.existsSync()) {
|
||||
file.deleteSync();
|
||||
@ -146,7 +129,7 @@ void main() {
|
||||
});
|
||||
|
||||
testUsingContext('androidComponentSetup all files exist passes', () async {
|
||||
final Directory templatesDir = env.flutterRootDir.childDirectory('templates').childDirectory('deferred_component');
|
||||
final Directory templatesDir = flutterRootDir.childDirectory('templates').childDirectory('deferred_component');
|
||||
final File buildGradleTemplate = templatesDir.childFile('build.gradle.tmpl');
|
||||
final File androidManifestTemplate = templatesDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml.tmpl');
|
||||
if (templatesDir.existsSync()) {
|
||||
@ -158,12 +141,13 @@ void main() {
|
||||
androidManifestTemplate.writeAsStringSync('fake AndroidManigest.xml template {{componentName}}', flush: true, mode: FileMode.append);
|
||||
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
env,
|
||||
projectDir,
|
||||
logger,
|
||||
exitOnFail: false,
|
||||
title: 'test check',
|
||||
templatesDir: templatesDir,
|
||||
);
|
||||
final Directory componentDir = env.projectDir.childDirectory('android').childDirectory('component1');
|
||||
final Directory componentDir = projectDir.childDirectory('android').childDirectory('component1');
|
||||
final File buildGradle = componentDir.childFile('build.gradle');
|
||||
if (buildGradle.existsSync()) {
|
||||
buildGradle.deleteSync();
|
||||
@ -189,11 +173,12 @@ void main() {
|
||||
|
||||
testWithoutContext('androidStringMapping creates new file', () async {
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
env,
|
||||
projectDir,
|
||||
logger,
|
||||
exitOnFail: false,
|
||||
title: 'test check',
|
||||
);
|
||||
final Directory baseModuleDir = env.projectDir.childDirectory('android').childDirectory('app');
|
||||
final Directory baseModuleDir = projectDir.childDirectory('android').childDirectory('app');
|
||||
final File stringRes = baseModuleDir.childDirectory('src').childDirectory('main').childDirectory('res').childDirectory('values').childFile('strings.xml');
|
||||
if (stringRes.existsSync()) {
|
||||
stringRes.deleteSync();
|
||||
@ -239,7 +224,7 @@ void main() {
|
||||
expect(logger.statusText.contains('Newly generated android files:\n'), true);
|
||||
expect(logger.statusText.contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/res/values/strings.xml\n'), true);
|
||||
|
||||
final File stringsOutput = env.projectDir
|
||||
final File stringsOutput = projectDir
|
||||
.childDirectory('build')
|
||||
.childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory)
|
||||
.childDirectory('app')
|
||||
@ -255,11 +240,12 @@ void main() {
|
||||
|
||||
testWithoutContext('androidStringMapping modifies strings file', () async {
|
||||
final DeferredComponentsPrebuildValidator validator = DeferredComponentsPrebuildValidator(
|
||||
env,
|
||||
projectDir,
|
||||
logger,
|
||||
exitOnFail: false,
|
||||
title: 'test check',
|
||||
);
|
||||
final Directory baseModuleDir = env.projectDir.childDirectory('android').childDirectory('app');
|
||||
final Directory baseModuleDir = projectDir.childDirectory('android').childDirectory('app');
|
||||
final File stringRes = baseModuleDir.childDirectory('src').childDirectory('main').childDirectory('res').childDirectory('values').childFile('strings.xml');
|
||||
if (stringRes.existsSync()) {
|
||||
stringRes.deleteSync();
|
||||
@ -285,7 +271,7 @@ void main() {
|
||||
expect(logger.statusText.contains('Modified android files:\n'), true);
|
||||
expect(logger.statusText.contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/res/values/strings.xml\n'), true);
|
||||
|
||||
final File stringsOutput = env.projectDir
|
||||
final File stringsOutput = projectDir
|
||||
.childDirectory('build')
|
||||
.childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory)
|
||||
.childDirectory('app')
|
||||
|
@ -176,6 +176,129 @@ flutter:
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('deferred assets are parsed', () async {
|
||||
globals.fs.file('.packages').createSync();
|
||||
globals.fs.file(globals.fs.path.join('assets', 'foo', 'bar.txt')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('assets', 'bar', 'barbie.txt')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('assets', 'wild', 'dash.txt')).createSync(recursive: true);
|
||||
globals.fs.file('pubspec.yaml')
|
||||
..createSync()
|
||||
..writeAsStringSync(r'''
|
||||
name: example
|
||||
flutter:
|
||||
assets:
|
||||
- assets/foo/
|
||||
deferred-components:
|
||||
- name: component1
|
||||
assets:
|
||||
- assets/bar/barbie.txt
|
||||
- assets/wild/
|
||||
''');
|
||||
final AssetBundle bundle = AssetBundleFactory.defaultInstance(
|
||||
logger: globals.logger,
|
||||
fileSystem: globals.fs,
|
||||
platform: globals.platform,
|
||||
splitDeferredAssets: true,
|
||||
).createBundle();
|
||||
await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages', deferredComponentsEnabled: true);
|
||||
// Expected assets:
|
||||
// - asset manifest
|
||||
// - font manifest
|
||||
// - license file
|
||||
// - assets/foo/bar.txt
|
||||
expect(bundle.entries.length, 4);
|
||||
expect(bundle.deferredComponentsEntries.length, 1);
|
||||
expect(bundle.deferredComponentsEntries['component1'].length, 2);
|
||||
expect(bundle.needsBuild(manifestPath: 'pubspec.yaml'), false);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('deferred assets are parsed regularly when splitDeferredAssets Disabled', () async {
|
||||
globals.fs.file('.packages').createSync();
|
||||
globals.fs.file(globals.fs.path.join('assets', 'foo', 'bar.txt')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('assets', 'bar', 'barbie.txt')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('assets', 'wild', 'dash.txt')).createSync(recursive: true);
|
||||
globals.fs.file('pubspec.yaml')
|
||||
..createSync()
|
||||
..writeAsStringSync(r'''
|
||||
name: example
|
||||
flutter:
|
||||
assets:
|
||||
- assets/foo/
|
||||
deferred-components:
|
||||
- name: component1
|
||||
assets:
|
||||
- assets/bar/barbie.txt
|
||||
- assets/wild/
|
||||
''');
|
||||
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
|
||||
await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages', deferredComponentsEnabled: false);
|
||||
// Expected assets:
|
||||
// - asset manifest
|
||||
// - font manifest
|
||||
// - license file
|
||||
// - assets/foo/bar.txt
|
||||
expect(bundle.entries.length, 6);
|
||||
expect(bundle.deferredComponentsEntries.isEmpty, true);
|
||||
expect(bundle.needsBuild(manifestPath: 'pubspec.yaml'), false);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('deferred assets wildcard parsed', () async {
|
||||
final File packageFile = globals.fs.file('.packages')..createSync();
|
||||
globals.fs.file(globals.fs.path.join('assets', 'foo', 'bar.txt')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('assets', 'bar', 'barbie.txt')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('assets', 'wild', 'dash.txt')).createSync(recursive: true);
|
||||
globals.fs.file('pubspec.yaml')
|
||||
..createSync()
|
||||
..writeAsStringSync(r'''
|
||||
name: example
|
||||
flutter:
|
||||
assets:
|
||||
- assets/foo/
|
||||
deferred-components:
|
||||
- name: component1
|
||||
assets:
|
||||
- assets/bar/barbie.txt
|
||||
- assets/wild/
|
||||
''');
|
||||
final AssetBundle bundle = AssetBundleFactory.defaultInstance(
|
||||
logger: globals.logger,
|
||||
fileSystem: globals.fs,
|
||||
platform: globals.platform,
|
||||
splitDeferredAssets: true,
|
||||
).createBundle();
|
||||
await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages', deferredComponentsEnabled: true);
|
||||
// Expected assets:
|
||||
// - asset manifest
|
||||
// - font manifest
|
||||
// - license file
|
||||
// - assets/foo/bar.txt
|
||||
expect(bundle.entries.length, 4);
|
||||
expect(bundle.deferredComponentsEntries.length, 1);
|
||||
expect(bundle.deferredComponentsEntries['component1'].length, 2);
|
||||
expect(bundle.needsBuild(manifestPath: 'pubspec.yaml'), false);
|
||||
|
||||
// Simulate modifying the files by updating the filestat time manually.
|
||||
globals.fs.file(globals.fs.path.join('assets', 'wild', 'fizz.txt'))
|
||||
..createSync(recursive: true)
|
||||
..setLastModifiedSync(packageFile.lastModifiedSync().add(const Duration(hours: 1)));
|
||||
|
||||
expect(bundle.needsBuild(manifestPath: 'pubspec.yaml'), true);
|
||||
await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages', deferredComponentsEnabled: true);
|
||||
|
||||
expect(bundle.entries.length, 4);
|
||||
expect(bundle.deferredComponentsEntries.length, 1);
|
||||
expect(bundle.deferredComponentsEntries['component1'].length, 3);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
});
|
||||
|
||||
testUsingContext('Failed directory delete shows message', () async {
|
||||
|
@ -130,16 +130,16 @@ void main() {
|
||||
wrapColumn: maxLineWidth,
|
||||
);
|
||||
|
||||
expect(commandHelp.L.toString(), endsWith('\x1B[1;30m(debugDumpLayerTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.P.toString(), endsWith('\x1B[1;30m(WidgetsApp.showPerformanceOverlay)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.S.toString(), endsWith('\x1B[1;30m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.U.toString(), endsWith('\x1B[1;30m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.a.toString(), endsWith('\x1B[1;30m(debugProfileWidgetBuilds)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.i.toString(), endsWith('\x1B[1;30m(WidgetsApp.showWidgetInspectorOverride)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.o.toString(), endsWith('\x1B[1;30m(defaultTargetPlatform)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.p.toString(), endsWith('\x1B[1;30m(debugPaintSizeEnabled)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.t.toString(), endsWith('\x1B[1;30m(debugDumpRenderTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.w.toString(), endsWith('\x1B[1;30m(debugDumpApp)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.L.toString(), endsWith('\x1B[90m(debugDumpLayerTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.P.toString(), endsWith('\x1B[90m(WidgetsApp.showPerformanceOverlay)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.S.toString(), endsWith('\x1B[90m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.U.toString(), endsWith('\x1B[90m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.a.toString(), endsWith('\x1B[90m(debugProfileWidgetBuilds)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.i.toString(), endsWith('\x1B[90m(WidgetsApp.showWidgetInspectorOverride)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.o.toString(), endsWith('\x1B[90m(defaultTargetPlatform)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.p.toString(), endsWith('\x1B[90m(debugPaintSizeEnabled)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.t.toString(), endsWith('\x1B[90m(debugDumpRenderTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.w.toString(), endsWith('\x1B[90m(debugDumpApp)\x1B[39m\x1b[22m'));
|
||||
});
|
||||
|
||||
testWithoutContext('should not create a help text longer than maxLineWidth without ansi support', () {
|
||||
@ -180,24 +180,24 @@ void main() {
|
||||
wrapColumn: maxLineWidth,
|
||||
);
|
||||
|
||||
expect(commandHelp.L.toString(), equals('\x1B[1mL\x1B[22m Dump layer tree to the console. \x1B[1;30m(debugDumpLayerTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.P.toString(), equals('\x1B[1mP\x1B[22m Toggle performance overlay. \x1B[1;30m(WidgetsApp.showPerformanceOverlay)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.L.toString(), equals('\x1B[1mL\x1B[22m Dump layer tree to the console. \x1B[90m(debugDumpLayerTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.P.toString(), equals('\x1B[1mP\x1B[22m Toggle performance overlay. \x1B[90m(WidgetsApp.showPerformanceOverlay)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.R.toString(), equals('\x1B[1mR\x1B[22m Hot restart.'));
|
||||
expect(commandHelp.S.toString(), equals('\x1B[1mS\x1B[22m Dump accessibility tree in traversal order. \x1B[1;30m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.U.toString(), equals('\x1B[1mU\x1B[22m Dump accessibility tree in inverse hit test order. \x1B[1;30m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.a.toString(), equals('\x1B[1ma\x1B[22m Toggle timeline events for all widget build methods. \x1B[1;30m(debugProfileWidgetBuilds)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.S.toString(), equals('\x1B[1mS\x1B[22m Dump accessibility tree in traversal order. \x1B[90m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.U.toString(), equals('\x1B[1mU\x1B[22m Dump accessibility tree in inverse hit test order. \x1B[90m(debugDumpSemantics)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.a.toString(), equals('\x1B[1ma\x1B[22m Toggle timeline events for all widget build methods. \x1B[90m(debugProfileWidgetBuilds)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.d.toString(), equals('\x1B[1md\x1B[22m Detach (terminate "flutter run" but leave application running).'));
|
||||
expect(commandHelp.g.toString(), equals('\x1B[1mg\x1B[22m Run source code generators.'));
|
||||
expect(commandHelp.h.toString(), equals('\x1B[1mh\x1B[22m Repeat this help message.'));
|
||||
expect(commandHelp.i.toString(), equals('\x1B[1mi\x1B[22m Toggle widget inspector. \x1B[1;30m(WidgetsApp.showWidgetInspectorOverride)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.o.toString(), equals('\x1B[1mo\x1B[22m Simulate different operating systems. \x1B[1;30m(defaultTargetPlatform)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.p.toString(), equals('\x1B[1mp\x1B[22m Toggle the display of construction lines. \x1B[1;30m(debugPaintSizeEnabled)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.i.toString(), equals('\x1B[1mi\x1B[22m Toggle widget inspector. \x1B[90m(WidgetsApp.showWidgetInspectorOverride)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.o.toString(), equals('\x1B[1mo\x1B[22m Simulate different operating systems. \x1B[90m(defaultTargetPlatform)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.p.toString(), equals('\x1B[1mp\x1B[22m Toggle the display of construction lines. \x1B[90m(debugPaintSizeEnabled)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.q.toString(), equals('\x1B[1mq\x1B[22m Quit (terminate the application on the device).'));
|
||||
expect(commandHelp.r.toString(), equals('\x1B[1mr\x1B[22m Hot reload. $fire$fire$fire'));
|
||||
expect(commandHelp.s.toString(), equals('\x1B[1ms\x1B[22m Save a screenshot to flutter.png.'));
|
||||
expect(commandHelp.t.toString(), equals('\x1B[1mt\x1B[22m Dump rendering tree to the console. \x1B[1;30m(debugDumpRenderTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.t.toString(), equals('\x1B[1mt\x1B[22m Dump rendering tree to the console. \x1B[90m(debugDumpRenderTree)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.v.toString(), equals('\x1B[1mv\x1B[22m Launch DevTools.'));
|
||||
expect(commandHelp.w.toString(), equals('\x1B[1mw\x1B[22m Dump widget hierarchy to the console. \x1B[1;30m(debugDumpApp)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.w.toString(), equals('\x1B[1mw\x1B[22m Dump widget hierarchy to the console. \x1B[90m(debugDumpApp)\x1B[39m\x1b[22m'));
|
||||
expect(commandHelp.z.toString(), equals('\x1B[1mz\x1B[22m Toggle elevation checker.'));
|
||||
});
|
||||
|
||||
|
@ -34,7 +34,7 @@ void main() {
|
||||
buildDir: fileSystem.directory('build')..createSync(),
|
||||
projectDir: fileSystem.directory('project')..createSync(),
|
||||
defines: <String, String>{
|
||||
kSplitAot: 'true',
|
||||
kDeferredComponents: 'true',
|
||||
},
|
||||
artifacts: null,
|
||||
processManager: null,
|
||||
@ -44,11 +44,10 @@ void main() {
|
||||
environment.buildDir.createSync(recursive: true);
|
||||
const AndroidAot androidAot = AndroidAot(TargetPlatform.android_arm64, BuildMode.release);
|
||||
const AndroidAotBundle androidAotBundle = AndroidAotBundle(androidAot);
|
||||
final CompositeTarget androidDefBundle = CompositeTarget(<Target>[androidAotBundle]);
|
||||
final CompositeTarget compositeTarget = CompositeTarget(<Target>[androidDefBundle]);
|
||||
final AndroidAotDeferredComponentsBundle androidDefBundle = AndroidAotDeferredComponentsBundle(androidAotBundle);
|
||||
final DeferredComponentsGenSnapshotValidatorTarget validatorTarget = DeferredComponentsGenSnapshotValidatorTarget(
|
||||
dependency: compositeTarget,
|
||||
abis: <String>['arm64-v8a'],
|
||||
deferredComponentsDependencies: <AndroidAotDeferredComponentsBundle>[androidDefBundle],
|
||||
nonDeferredComponentsDependencies: <Target>[],
|
||||
title: 'test checks',
|
||||
exitOnFail: false,
|
||||
);
|
||||
@ -68,7 +67,7 @@ void main() {
|
||||
buildDir: fileSystem.directory('build')..createSync(),
|
||||
projectDir: fileSystem.directory('project')..createSync(),
|
||||
defines: <String, String>{
|
||||
kSplitAot: 'true',
|
||||
kDeferredComponents: 'true',
|
||||
},
|
||||
artifacts: null,
|
||||
processManager: null,
|
||||
@ -78,11 +77,10 @@ void main() {
|
||||
environment.buildDir.createSync(recursive: true);
|
||||
const AndroidAot androidAot = AndroidAot(TargetPlatform.android_arm64, BuildMode.release);
|
||||
const AndroidAotBundle androidAotBundle = AndroidAotBundle(androidAot);
|
||||
final CompositeTarget androidDefBundle = CompositeTarget(<Target>[androidAotBundle]);
|
||||
final CompositeTarget compositeTarget = CompositeTarget(<Target>[androidDefBundle]);
|
||||
final AndroidAotDeferredComponentsBundle androidDefBundle = AndroidAotDeferredComponentsBundle(androidAotBundle);
|
||||
final DeferredComponentsGenSnapshotValidatorTarget validatorTarget = DeferredComponentsGenSnapshotValidatorTarget(
|
||||
dependency: compositeTarget,
|
||||
abis: <String>['arm64-v8a'],
|
||||
deferredComponentsDependencies: <AndroidAotDeferredComponentsBundle>[androidDefBundle],
|
||||
nonDeferredComponentsDependencies: <Target>[],
|
||||
title: 'test checks',
|
||||
exitOnFail: false,
|
||||
);
|
||||
@ -101,7 +99,7 @@ void main() {
|
||||
buildDir: fileSystem.directory('build')..createSync(),
|
||||
projectDir: fileSystem.directory('project')..createSync(),
|
||||
defines: <String, String>{
|
||||
kSplitAot: 'true',
|
||||
kDeferredComponents: 'true',
|
||||
},
|
||||
artifacts: null,
|
||||
processManager: null,
|
||||
@ -111,11 +109,10 @@ void main() {
|
||||
environment.buildDir.createSync(recursive: true);
|
||||
const AndroidAot androidAot = AndroidAot(TargetPlatform.android_arm64, BuildMode.release);
|
||||
const AndroidAotBundle androidAotBundle = AndroidAotBundle(androidAot);
|
||||
final CompositeTarget androidDefBundle = CompositeTarget(<Target>[androidAotBundle]);
|
||||
final CompositeTarget compositeTarget = CompositeTarget(<Target>[androidDefBundle]);
|
||||
final AndroidAotDeferredComponentsBundle androidDefBundle = AndroidAotDeferredComponentsBundle(androidAotBundle);
|
||||
final DeferredComponentsGenSnapshotValidatorTarget validatorTarget = DeferredComponentsGenSnapshotValidatorTarget(
|
||||
dependency: compositeTarget,
|
||||
abis: <String>['arm64-v8a'],
|
||||
deferredComponentsDependencies: <AndroidAotDeferredComponentsBundle>[androidDefBundle],
|
||||
nonDeferredComponentsDependencies: <Target>[],
|
||||
title: 'test checks',
|
||||
exitOnFail: false,
|
||||
);
|
||||
|
@ -0,0 +1,274 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file_testing/file_testing.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import 'test_data/deferred_components_project.dart';
|
||||
import 'test_driver.dart';
|
||||
import 'test_utils.dart';
|
||||
|
||||
void main() {
|
||||
Directory tempDir;
|
||||
FlutterRunTestDriver _flutter;
|
||||
|
||||
setUp(() async {
|
||||
tempDir = createResolvedTempDirectorySync('run_test.');
|
||||
_flutter = FlutterRunTestDriver(tempDir);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await _flutter.stop();
|
||||
tryToDelete(tempDir);
|
||||
});
|
||||
|
||||
testWithoutContext('simple build appbundle android-arm64 target succeeds', () async {
|
||||
final DeferredComponentsProject project = DeferredComponentsProject(BasicDeferredComponentsConfig());
|
||||
await project.setUpIn(tempDir);
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
final ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
...getLocalEngineArguments(),
|
||||
'build',
|
||||
'appbundle',
|
||||
'--target-platform=android-arm64'
|
||||
], workingDirectory: tempDir.path);
|
||||
|
||||
expect(result.stdout.toString(), contains('app-release.aab'));
|
||||
expect(result.stdout.toString(), contains('Deferred components prebuild validation passed.'));
|
||||
expect(result.stdout.toString(), contains('Deferred components gen_snapshot validation passed.'));
|
||||
|
||||
final String line = result.stdout.toString()
|
||||
.split('\n')
|
||||
.firstWhere((String line) => line.contains('app-release.aab'));
|
||||
|
||||
final String outputFilePath = line.split(' ')[2].trim();
|
||||
final File outputFile = fileSystem.file(fileSystem.path.join(tempDir.path, outputFilePath));
|
||||
expect(outputFile, exists);
|
||||
|
||||
final Archive archive = ZipDecoder().decodeBytes(outputFile.readAsBytesSync());
|
||||
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/arm64-v8a/libapp.so-2.part.so') != null, true);
|
||||
|
||||
expect(archive.findFile('component1/assets/flutter_assets/test_assets/asset2.txt') != null, true);
|
||||
expect(archive.findFile('base/assets/flutter_assets/test_assets/asset1.txt') != null, true);
|
||||
|
||||
expect(result.exitCode, 0);
|
||||
}, timeout: const Timeout(Duration(minutes: 2)));
|
||||
|
||||
testWithoutContext('simple build appbundle all targets succeeds', () async {
|
||||
final DeferredComponentsProject project = DeferredComponentsProject(BasicDeferredComponentsConfig());
|
||||
await project.setUpIn(tempDir);
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
final ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
...getLocalEngineArguments(),
|
||||
'build',
|
||||
'appbundle',
|
||||
], workingDirectory: tempDir.path);
|
||||
|
||||
expect(result.stdout.toString(), contains('app-release.aab'));
|
||||
expect(result.stdout.toString(), contains('Deferred components prebuild validation passed.'));
|
||||
expect(result.stdout.toString(), contains('Deferred components gen_snapshot validation passed.'));
|
||||
|
||||
final String line = result.stdout.toString()
|
||||
.split('\n')
|
||||
.firstWhere((String line) => line.contains('app-release.aab'));
|
||||
|
||||
final String outputFilePath = line.split(' ')[2].trim();
|
||||
final File outputFile = fileSystem.file(fileSystem.path.join(tempDir.path, outputFilePath));
|
||||
expect(outputFile, exists);
|
||||
|
||||
final Archive archive = ZipDecoder().decodeBytes(outputFile.readAsBytesSync());
|
||||
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/arm64-v8a/libapp.so-2.part.so') != null, true);
|
||||
|
||||
expect(archive.findFile('base/lib/armeabi-v7a/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/armeabi-v7a/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/armeabi-v7a/libapp.so-2.part.so') != null, true);
|
||||
|
||||
expect(archive.findFile('base/lib/x86_64/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/x86_64/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/x86_64/libapp.so-2.part.so') != null, true);
|
||||
|
||||
expect(archive.findFile('component1/assets/flutter_assets/test_assets/asset2.txt') != null, true);
|
||||
expect(archive.findFile('base/assets/flutter_assets/test_assets/asset1.txt') != null, true);
|
||||
|
||||
expect(result.exitCode, 0);
|
||||
}, timeout: const Timeout(Duration(minutes: 3)));
|
||||
|
||||
testWithoutContext('simple build appbundle no-deferred-components succeeds', () async {
|
||||
final DeferredComponentsProject project = DeferredComponentsProject(BasicDeferredComponentsConfig());
|
||||
await project.setUpIn(tempDir);
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
final ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
...getLocalEngineArguments(),
|
||||
'build',
|
||||
'appbundle',
|
||||
'--no-deferred-components'
|
||||
], workingDirectory: tempDir.path);
|
||||
|
||||
expect(result.stdout.toString().contains('app-release.aab'), true);
|
||||
expect(result.stdout.toString().contains('Deferred components prebuild validation passed.'), false);
|
||||
expect(result.stdout.toString().contains('Deferred components gen_snapshot validation passed.'), false);
|
||||
|
||||
final String line = result.stdout.toString()
|
||||
.split('\n')
|
||||
.firstWhere((String line) => line.contains('app-release.aab'));
|
||||
|
||||
final String outputFilePath = line.split(' ')[2].trim();
|
||||
final File outputFile = fileSystem.file(fileSystem.path.join(tempDir.path, outputFilePath));
|
||||
expect(outputFile, exists);
|
||||
|
||||
final Archive archive = ZipDecoder().decodeBytes(outputFile.readAsBytesSync());
|
||||
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/arm64-v8a/libapp.so-2.part.so') != null, false);
|
||||
|
||||
expect(archive.findFile('base/lib/armeabi-v7a/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/armeabi-v7a/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/armeabi-v7a/libapp.so-2.part.so') != null, false);
|
||||
|
||||
expect(archive.findFile('base/lib/x86_64/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/x86_64/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/x86_64/libapp.so-2.part.so') != null, false);
|
||||
|
||||
// Asset 2 is merged into the base module assets.
|
||||
expect(archive.findFile('component1/assets/flutter_assets/test_assets/asset2.txt') != null, false);
|
||||
expect(archive.findFile('base/assets/flutter_assets/test_assets/asset2.txt') != null, true);
|
||||
expect(archive.findFile('base/assets/flutter_assets/test_assets/asset1.txt') != null, true);
|
||||
|
||||
expect(result.exitCode, 0);
|
||||
}, timeout: const Timeout(Duration(minutes: 3)));
|
||||
|
||||
testWithoutContext('simple build appbundle mismatched golden no-validate-deferred-components succeeds', () async {
|
||||
final DeferredComponentsProject project = DeferredComponentsProject(MismatchedGoldenDeferredComponentsConfig());
|
||||
await project.setUpIn(tempDir);
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
final ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
...getLocalEngineArguments(),
|
||||
'build',
|
||||
'appbundle',
|
||||
'--no-validate-deferred-components',
|
||||
], workingDirectory: tempDir.path);
|
||||
|
||||
expect(result.stdout.toString().contains('app-release.aab'), true);
|
||||
expect(result.stdout.toString().contains('Deferred components prebuild validation passed.'), false);
|
||||
expect(result.stdout.toString().contains('Deferred components gen_snapshot validation passed.'), false);
|
||||
|
||||
expect(result.stdout.toString().contains('New loading units were found:'), false);
|
||||
expect(result.stdout.toString().contains('Previously existing loading units no longer exist:'), false);
|
||||
|
||||
final String line = result.stdout.toString()
|
||||
.split('\n')
|
||||
.firstWhere((String line) => line.contains('app-release.aab'));
|
||||
|
||||
final String outputFilePath = line.split(' ')[2].trim();
|
||||
final File outputFile = fileSystem.file(fileSystem.path.join(tempDir.path, outputFilePath));
|
||||
expect(outputFile, exists);
|
||||
|
||||
final Archive archive = ZipDecoder().decodeBytes(outputFile.readAsBytesSync());
|
||||
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/arm64-v8a/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/arm64-v8a/libapp.so-2.part.so') != null, true);
|
||||
|
||||
expect(archive.findFile('base/lib/armeabi-v7a/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/armeabi-v7a/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/armeabi-v7a/libapp.so-2.part.so') != null, true);
|
||||
|
||||
expect(archive.findFile('base/lib/x86_64/libapp.so') != null, true);
|
||||
expect(archive.findFile('base/lib/x86_64/libflutter.so') != null, true);
|
||||
expect(archive.findFile('component1/lib/x86_64/libapp.so-2.part.so') != null, true);
|
||||
|
||||
expect(archive.findFile('component1/assets/flutter_assets/test_assets/asset2.txt') != null, true);
|
||||
expect(archive.findFile('base/assets/flutter_assets/test_assets/asset1.txt') != null, true);
|
||||
|
||||
expect(result.exitCode, 0);
|
||||
}, timeout: const Timeout(Duration(minutes: 3)));
|
||||
|
||||
testWithoutContext('simple build appbundle missing android dynamic feature module fails', () async {
|
||||
final DeferredComponentsProject project = DeferredComponentsProject(NoAndroidDynamicFeatureModuleDeferredComponentsConfig());
|
||||
await project.setUpIn(tempDir);
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
final ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
...getLocalEngineArguments(),
|
||||
'build',
|
||||
'appbundle',
|
||||
], workingDirectory: tempDir.path);
|
||||
|
||||
expect(result.stdout.toString().contains('app-release.aab'), false);
|
||||
expect(result.stdout.toString().contains('Deferred components prebuild validation passed.'), false);
|
||||
expect(result.stdout.toString().contains('Deferred components gen_snapshot validation passed.'), false);
|
||||
|
||||
expect(result.stdout.toString(), contains('Newly generated android files:'));
|
||||
final String pathSeparator = fileSystem.path.separator;
|
||||
expect(result.stdout.toString(), contains('build${pathSeparator}android_deferred_components_setup_files${pathSeparator}component1${pathSeparator}build.gradle'));
|
||||
expect(result.stdout.toString(), contains('build${pathSeparator}android_deferred_components_setup_files${pathSeparator}component1${pathSeparator}src${pathSeparator}main${pathSeparator}AndroidManifest.xml'));
|
||||
|
||||
expect(result.exitCode, 1);
|
||||
});
|
||||
|
||||
testWithoutContext('simple build appbundle missing golden fails', () async {
|
||||
final DeferredComponentsProject project = DeferredComponentsProject(NoGoldenDeferredComponentsConfig());
|
||||
await project.setUpIn(tempDir);
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
final ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
...getLocalEngineArguments(),
|
||||
'build',
|
||||
'appbundle',
|
||||
], workingDirectory: tempDir.path);
|
||||
|
||||
expect(result.stdout.toString().contains('app-release.aab'), false);
|
||||
expect(result.stdout.toString().contains('Deferred components prebuild validation passed.'), true);
|
||||
expect(result.stdout.toString().contains('Deferred components gen_snapshot validation passed.'), false);
|
||||
|
||||
expect(result.stdout.toString(), contains('New loading units were found:'));
|
||||
expect(result.stdout.toString(), contains('- package:test/deferred_library.dart'));
|
||||
|
||||
expect(result.stdout.toString().contains('Previously existing loading units no longer exist:'), false);
|
||||
|
||||
expect(result.exitCode, 1);
|
||||
});
|
||||
|
||||
testWithoutContext('simple build appbundle mismatched golden fails', () async {
|
||||
final DeferredComponentsProject project = DeferredComponentsProject(MismatchedGoldenDeferredComponentsConfig());
|
||||
await project.setUpIn(tempDir);
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
final ProcessResult result = await processManager.run(<String>[
|
||||
flutterBin,
|
||||
...getLocalEngineArguments(),
|
||||
'build',
|
||||
'appbundle',
|
||||
], workingDirectory: tempDir.path);
|
||||
|
||||
expect(result.stdout.toString().contains('app-release.aab'), false);
|
||||
expect(result.stdout.toString().contains('Deferred components prebuild validation passed.'), true);
|
||||
expect(result.stdout.toString().contains('Deferred components gen_snapshot validation passed.'), false);
|
||||
|
||||
expect(result.stdout.toString(), contains('New loading units were found:'));
|
||||
expect(result.stdout.toString(), contains('- package:test/deferred_library.dart'));
|
||||
|
||||
expect(result.stdout.toString(), contains('Previously existing loading units no longer exist:'));
|
||||
expect(result.stdout.toString(), contains('- package:test/invalid_lib_name.dart'));
|
||||
|
||||
expect(result.stdout.toString(), contains('This loading unit check will not fail again on the next build attempt'));
|
||||
|
||||
expect(result.exitCode, 1);
|
||||
});
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'package:file/file.dart';
|
||||
|
||||
import '../test_utils.dart';
|
||||
|
||||
abstract class DeferredComponentsConfig {
|
||||
String get deferredLibrary;
|
||||
String get deferredComponentsGolden;
|
||||
String get androidSettings;
|
||||
String get androidBuild;
|
||||
String get androidLocalProperties;
|
||||
String get androidGradleProperties;
|
||||
String get androidKeyProperties;
|
||||
List<int> get androidKey;
|
||||
String get appBuild;
|
||||
String get appManifest;
|
||||
String get appStrings;
|
||||
String get appStyles;
|
||||
String get appLaunchBackground;
|
||||
String get asset1;
|
||||
String get asset2;
|
||||
List<DeferredComponentModule> get deferredComponents;
|
||||
|
||||
void setUpIn(Directory dir) {
|
||||
if (deferredLibrary != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'lib', 'deferred_library.dart'), deferredLibrary);
|
||||
}
|
||||
if (deferredComponentsGolden != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'deferred_components_loading_units.yaml'), deferredComponentsGolden);
|
||||
}
|
||||
if (androidSettings != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'settings.gradle'), androidSettings);
|
||||
}
|
||||
if (androidBuild != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'build.gradle'), androidBuild);
|
||||
}
|
||||
if (androidLocalProperties != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'local.properties'), androidLocalProperties);
|
||||
}
|
||||
if (androidGradleProperties != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'gradle.properties'), androidGradleProperties);
|
||||
}
|
||||
if (androidKeyProperties != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'key.properties'), androidKeyProperties);
|
||||
}
|
||||
if (androidKey != null) {
|
||||
writeBytesFile(fileSystem.path.join(dir.path, 'android', 'app', 'key.jks'), androidKey);
|
||||
}
|
||||
if (appBuild != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'app', 'build.gradle'), appBuild);
|
||||
}
|
||||
if (appManifest != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'app', 'src', 'main', 'AndroidManifest.xml'), appManifest);
|
||||
}
|
||||
if (appStrings != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'app', 'src', 'main', 'res', 'values', 'strings.xml'), appStrings);
|
||||
}
|
||||
if (appStyles != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'app', 'src', 'main', 'res', 'values', 'styles.xml'), appStyles);
|
||||
}
|
||||
if (appLaunchBackground != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', 'app', 'src', 'main', 'res', 'drawable', 'launch_background.xml'), appLaunchBackground);
|
||||
}
|
||||
if (asset1 != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'test_assets/asset1.txt'), asset1);
|
||||
}
|
||||
if (asset2 != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'test_assets/asset2.txt'), asset2);
|
||||
}
|
||||
if (deferredComponents != null) {
|
||||
for (final DeferredComponentModule component in deferredComponents) {
|
||||
component.setUpIn(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DeferredComponentModule {
|
||||
DeferredComponentModule(this.name);
|
||||
|
||||
String name;
|
||||
|
||||
void setUpIn(Directory dir) {
|
||||
if (name != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', name, 'build.gradle'), r'''
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: "com.android.dynamic-feature"
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
sourceSets {
|
||||
applicationVariants.all { variant ->
|
||||
main.assets.srcDirs += "${project.buildDir}/intermediates/flutter/${variant.name}/deferred_assets"
|
||||
main.jniLibs.srcDirs += "${project.buildDir}/intermediates/flutter/${variant.name}/deferred_libs"
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility 1.8
|
||||
targetCompatibility 1.8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(":app")
|
||||
}
|
||||
''');
|
||||
|
||||
writeFile(fileSystem.path.join(dir.path, 'android', name, 'src', 'main', 'AndroidManifest.xml'), '''
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:dist="http://schemas.android.com/apk/distribution"
|
||||
package="com.example.$name">
|
||||
|
||||
<dist:module
|
||||
dist:instant="false"
|
||||
dist:title="@string/component1Name">
|
||||
<dist:delivery>
|
||||
<dist:on-demand />
|
||||
</dist:delivery>
|
||||
<dist:fusing dist:include="true" />
|
||||
</dist:module>
|
||||
</manifest>
|
||||
''');
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,619 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import '../../src/common.dart';
|
||||
import 'deferred_components_config.dart';
|
||||
import 'project.dart';
|
||||
|
||||
class DeferredComponentsProject extends Project {
|
||||
DeferredComponentsProject(this.deferredComponents);
|
||||
|
||||
@override
|
||||
final String pubspec = '''
|
||||
name: test
|
||||
environment:
|
||||
sdk: ">=2.12.0-0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
assets:
|
||||
- test_assets/asset1.txt
|
||||
deferred-components:
|
||||
- name: component1
|
||||
libraries:
|
||||
- package:test/deferred_library.dart
|
||||
assets:
|
||||
- test_assets/asset2.txt
|
||||
''';
|
||||
|
||||
@override
|
||||
final String main = r'''
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'deferred_library.dart' deferred as DeferredLibrary;
|
||||
|
||||
Future<void>? libFuture;
|
||||
String deferredText = 'incomplete';
|
||||
|
||||
Future<void> main() async {
|
||||
while (true) {
|
||||
if (libFuture == null) {
|
||||
libFuture = DeferredLibrary.loadLibrary();
|
||||
libFuture?.whenComplete(() => deferredText = 'complete ${DeferredLibrary.add(10, 42)}');
|
||||
}
|
||||
runApp(new MyApp());
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
}
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
topLevelFunction();
|
||||
return new MaterialApp( // BUILD BREAKPOINT
|
||||
title: 'Flutter Demo',
|
||||
home: new Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
topLevelFunction() {
|
||||
print(deferredText); // TOP LEVEL BREAKPOINT
|
||||
}
|
||||
''';
|
||||
|
||||
@override
|
||||
final DeferredComponentsConfig deferredComponents;
|
||||
}
|
||||
|
||||
/// Contains the necessary files for a bare-bones deferred component release app.
|
||||
class BasicDeferredComponentsConfig extends DeferredComponentsConfig {
|
||||
@override
|
||||
String get deferredLibrary => r'''
|
||||
library DeferredLibrary;
|
||||
|
||||
int add(int i, int j) {
|
||||
return i + j;
|
||||
}
|
||||
''';
|
||||
|
||||
@override
|
||||
String get deferredComponentsGolden => r'''
|
||||
loading-units:
|
||||
- id: 2
|
||||
libraries:
|
||||
- package:test/deferred_library.dart
|
||||
''';
|
||||
|
||||
@override
|
||||
String get androidSettings => r'''
|
||||
include ':app', ':component1'
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
''';
|
||||
|
||||
@override
|
||||
String get androidBuild => r'''
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
''';
|
||||
|
||||
@override
|
||||
String get appBuild => r'''
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
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.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "ninja.qian.splitaottest1"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
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.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "com.google.android.play:core:1.8.0"
|
||||
}
|
||||
|
||||
''';
|
||||
|
||||
@override
|
||||
String get androidLocalProperties => '''
|
||||
flutter.sdk=${getFlutterRoot()}
|
||||
flutter.buildMode=release
|
||||
flutter.versionName=1.0.0
|
||||
flutter.versionCode=22
|
||||
''';
|
||||
|
||||
@override
|
||||
String get androidGradleProperties => '''
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.enableR8=true
|
||||
android.experimental.enableNewResourceShrinker=true
|
||||
''';
|
||||
|
||||
@override
|
||||
String get androidKeyProperties => '''
|
||||
storePassword=123456
|
||||
keyPassword=123456
|
||||
keyAlias=test_release_key
|
||||
storeFile=key.jks
|
||||
''';
|
||||
|
||||
// This is a test jks keystore, generated for testing use only. Do not use this key in an actual
|
||||
// application.
|
||||
@override
|
||||
final List<int> androidKey = <int>[
|
||||
0xfe, 0xed, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x10, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x6b,
|
||||
0x65, 0x79, 0x00, 0x00, 0x01, 0x77, 0x87, 0xc9, 0x8c, 0x4f, 0x00, 0x00, 0x05, 0x00, 0x30, 0x82,
|
||||
0x04, 0xfc, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01,
|
||||
0x05, 0x00, 0x04, 0x82, 0x04, 0xe8, 0xe2, 0xca, 0x1f, 0xd5, 0x47, 0xf1, 0x6a, 0x4a, 0xc5, 0xf8,
|
||||
0x0b, 0xc1, 0x11, 0x36, 0xfc, 0xb3, 0x25, 0xb3, 0x54, 0x34, 0x53, 0x2c, 0x71, 0x81, 0xd6, 0x64,
|
||||
0x54, 0xef, 0x5f, 0x85, 0x27, 0xbe, 0xe5, 0x0a, 0x08, 0xc0, 0x76, 0x2d, 0xec, 0xbf, 0x82, 0x9f,
|
||||
0xf9, 0xf0, 0xb3, 0x20, 0x86, 0x9b, 0x25, 0x01, 0x02, 0x15, 0xa8, 0x78, 0x53, 0xd9, 0x97, 0xb8,
|
||||
0x15, 0x84, 0xad, 0x21, 0xe7, 0x04, 0x01, 0x53, 0xc0, 0x8f, 0x14, 0x0c, 0x45, 0xe0, 0x7a, 0x4e,
|
||||
0x95, 0x4e, 0xa9, 0xcd, 0x23, 0xbf, 0x78, 0xcd, 0x10, 0xd3, 0x09, 0xec, 0xfd, 0x64, 0x8d, 0xec,
|
||||
0xe8, 0x02, 0x3e, 0x5a, 0x04, 0x0b, 0xd3, 0x57, 0x66, 0xb9, 0xd0, 0x28, 0xcf, 0x28, 0x04, 0x6e,
|
||||
0x45, 0x67, 0x81, 0xb9, 0x1a, 0x64, 0xd6, 0x05, 0xf5, 0x3d, 0x90, 0x8e, 0x2d, 0x52, 0x95, 0x51,
|
||||
0x5c, 0x26, 0xcc, 0x43, 0x86, 0x7f, 0x07, 0x8c, 0xf8, 0x06, 0x25, 0xff, 0x53, 0xb4, 0x2b, 0x87,
|
||||
0xef, 0x93, 0xac, 0x99, 0xc3, 0x35, 0x6e, 0x1c, 0xf0, 0x9f, 0xb1, 0xda, 0x30, 0x88, 0x1a, 0x50,
|
||||
0xa5, 0x53, 0x39, 0xef, 0x4b, 0x5c, 0xc7, 0x72, 0xc6, 0xe2, 0xf6, 0x2e, 0xf3, 0xcc, 0xc8, 0xd8,
|
||||
0x80, 0xe5, 0x64, 0x45, 0xcb, 0xcc, 0x8c, 0x93, 0x7e, 0x00, 0x49, 0x3f, 0x27, 0xd8, 0xa1, 0xb2,
|
||||
0xa4, 0x7c, 0xc7, 0x39, 0xc9, 0x27, 0x1a, 0x2e, 0xca, 0x88, 0x7f, 0xf1, 0xf1, 0xad, 0x68, 0xb9,
|
||||
0x6b, 0xd8, 0xfe, 0xf1, 0xda, 0xad, 0x2d, 0xb2, 0x08, 0x33, 0x42, 0x12, 0x4b, 0x1f, 0x93, 0x17,
|
||||
0x7d, 0x50, 0xba, 0x68, 0x86, 0xdd, 0x61, 0x74, 0x0c, 0xed, 0x55, 0x60, 0x90, 0x01, 0x93, 0x11,
|
||||
0x13, 0xc2, 0x39, 0x78, 0xf6, 0xa4, 0x28, 0x8f, 0xa6, 0x63, 0x35, 0x28, 0xbd, 0xa1, 0x95, 0xc5,
|
||||
0x31, 0xa2, 0xae, 0x59, 0x67, 0x58, 0x08, 0x57, 0x45, 0xf3, 0x27, 0xfe, 0x83, 0x6a, 0xb3, 0x56,
|
||||
0x2c, 0x9b, 0xae, 0xf7, 0x78, 0x88, 0x88, 0xd4, 0xbe, 0x67, 0x11, 0x6a, 0x64, 0x39, 0x99, 0xaf,
|
||||
0xad, 0xc5, 0xd6, 0x3e, 0x30, 0x91, 0xee, 0x94, 0xdc, 0x50, 0x33, 0x57, 0x80, 0x1c, 0x4d, 0x80,
|
||||
0xda, 0x07, 0xea, 0x8c, 0x37, 0x26, 0x0e, 0xfe, 0xbc, 0x8c, 0x7a, 0xf7, 0x33, 0x3b, 0x85, 0x7c,
|
||||
0xc9, 0xf6, 0x44, 0x15, 0xc1, 0xa6, 0x95, 0x1e, 0x3e, 0x4a, 0x70, 0xb1, 0x31, 0xae, 0x1f, 0x61,
|
||||
0xad, 0x59, 0xc3, 0xc9, 0xa2, 0x0e, 0x11, 0xb8, 0x25, 0x84, 0x90, 0x66, 0x0c, 0x4b, 0x4a, 0xf4,
|
||||
0xec, 0xc0, 0x6a, 0x95, 0x81, 0x22, 0xfc, 0xd1, 0xda, 0xd0, 0x4f, 0x8a, 0x39, 0x6f, 0x24, 0x49,
|
||||
0xd7, 0xa6, 0x82, 0xbd, 0x8d, 0x5e, 0xbd, 0xe2, 0x69, 0x9b, 0xb4, 0xde, 0x37, 0x6a, 0x02, 0x7e,
|
||||
0x40, 0x8c, 0x3c, 0x34, 0x97, 0xfd, 0xc9, 0xc5, 0x75, 0x74, 0xa5, 0x04, 0x93, 0xa4, 0x04, 0x64,
|
||||
0x9f, 0xfd, 0xe2, 0x57, 0x63, 0x11, 0x5e, 0x51, 0x25, 0x36, 0xcc, 0x25, 0xe0, 0xda, 0x6d, 0x82,
|
||||
0xfe, 0xd2, 0x7b, 0x40, 0x82, 0x33, 0x69, 0xb0, 0xe8, 0x91, 0xb7, 0x23, 0xac, 0x22, 0x85, 0x98,
|
||||
0x3e, 0x02, 0x81, 0xa5, 0x4e, 0xaf, 0x99, 0xf1, 0x1b, 0x73, 0x38, 0xcd, 0x4c, 0xaa, 0x33, 0xdc,
|
||||
0x49, 0xd6, 0xf7, 0xdc, 0xa6, 0x59, 0x38, 0x67, 0x89, 0x78, 0x26, 0x8e, 0x1c, 0xee, 0xbe, 0x8b,
|
||||
0x6c, 0xae, 0xcf, 0x26, 0x67, 0x2a, 0xe6, 0xbe, 0x24, 0xc3, 0x6f, 0x2b, 0x1a, 0xcf, 0xb6, 0xd0,
|
||||
0x82, 0x58, 0x05, 0x44, 0x19, 0x91, 0xfb, 0x9f, 0x53, 0x14, 0x4a, 0xf5, 0x84, 0x54, 0x57, 0x8e,
|
||||
0xcc, 0xec, 0x6f, 0x29, 0xdd, 0xa6, 0x38, 0xa4, 0x17, 0x0e, 0x86, 0x63, 0x62, 0xd6, 0x43, 0x1c,
|
||||
0xd5, 0xee, 0x93, 0x35, 0x2f, 0x66, 0x35, 0xc8, 0x33, 0x5c, 0x2b, 0xbe, 0xc7, 0xba, 0xf2, 0xd5,
|
||||
0xe6, 0x51, 0xe1, 0xac, 0xac, 0x80, 0x71, 0x05, 0xed, 0x8a, 0x2f, 0x47, 0x30, 0xb5, 0x6b, 0x3d,
|
||||
0x1d, 0x90, 0xe0, 0x82, 0x76, 0xba, 0x85, 0x7b, 0xb1, 0x78, 0xc1, 0x6f, 0x9c, 0xc1, 0x45, 0xfc,
|
||||
0x31, 0x51, 0x04, 0xf3, 0x80, 0x98, 0x9d, 0xd7, 0xd3, 0xef, 0x4b, 0x1a, 0xeb, 0x59, 0x62, 0x97,
|
||||
0x64, 0xcd, 0x4b, 0x7f, 0xaf, 0x07, 0x2a, 0xc1, 0x77, 0x4c, 0xa0, 0x29, 0x41, 0x80, 0x01, 0xca,
|
||||
0xc5, 0xb2, 0xe0, 0x6e, 0x30, 0x70, 0xc4, 0xcf, 0xf7, 0xd6, 0x7f, 0x1d, 0x84, 0x9e, 0x31, 0x4b,
|
||||
0xa8, 0xa8, 0x26, 0x60, 0x7f, 0x76, 0x4c, 0x75, 0x46, 0xcf, 0x86, 0x39, 0xbd, 0x5b, 0x99, 0x1b,
|
||||
0x0f, 0xa2, 0x1a, 0x94, 0x62, 0xe7, 0x95, 0x9c, 0xcb, 0x4d, 0x13, 0xa4, 0x84, 0x79, 0xec, 0xd3,
|
||||
0x7c, 0xbd, 0x3f, 0xb7, 0x22, 0xa9, 0x14, 0xf0, 0xd3, 0x66, 0xb0, 0xa9, 0xe7, 0xdf, 0x01, 0x47,
|
||||
0x5c, 0xb0, 0x8e, 0x49, 0xfa, 0xfd, 0xa9, 0x9f, 0xf4, 0x29, 0x7e, 0x0c, 0x6a, 0x9f, 0x67, 0x7f,
|
||||
0x38, 0xe0, 0xe6, 0x48, 0x0d, 0x51, 0x7d, 0x79, 0x0d, 0xb8, 0x27, 0xec, 0x6e, 0x99, 0x3e, 0x00,
|
||||
0xb2, 0x18, 0x8e, 0x8d, 0xbf, 0x89, 0xd2, 0x4b, 0xce, 0xcc, 0x64, 0xb7, 0xae, 0x4a, 0x34, 0x8d,
|
||||
0xe1, 0x73, 0x4e, 0x2c, 0x50, 0x7e, 0xc5, 0xc7, 0x14, 0xa6, 0x8c, 0x51, 0x5b, 0x4a, 0x94, 0xf2,
|
||||
0x16, 0x58, 0x11, 0x1c, 0xc4, 0x81, 0x9d, 0xfc, 0x5d, 0x57, 0xe2, 0x8e, 0xdb, 0x51, 0x99, 0x5f,
|
||||
0xd5, 0x71, 0x72, 0x3a, 0xa1, 0xe0, 0xc1, 0x6f, 0xdb, 0x0c, 0xf0, 0x91, 0x7c, 0xb7, 0xc0, 0xf5,
|
||||
0x6f, 0x6c, 0x3c, 0xea, 0x0e, 0xd3, 0xab, 0x13, 0xed, 0x1e, 0x55, 0x9e, 0xdf, 0xf7, 0x37, 0x75,
|
||||
0x38, 0xb5, 0x07, 0x97, 0x1d, 0x82, 0x5b, 0x28, 0x1f, 0xbd, 0xe3, 0x70, 0xdb, 0x18, 0x3f, 0x69,
|
||||
0x95, 0x84, 0x8f, 0x99, 0xc4, 0xa6, 0xed, 0x60, 0xfc, 0x1e, 0x3b, 0x4d, 0x63, 0x34, 0xeb, 0xb8,
|
||||
0xd6, 0x18, 0x24, 0xf5, 0xd9, 0x3b, 0xc3, 0xdd, 0x3e, 0x49, 0x55, 0x7a, 0xa3, 0x49, 0x2c, 0x58,
|
||||
0x2a, 0x9c, 0x11, 0x21, 0xdc, 0x82, 0xa5, 0xc2, 0xf7, 0x87, 0x3a, 0xd8, 0xe8, 0x03, 0x3a, 0x5c,
|
||||
0xdf, 0xac, 0xa7, 0xbe, 0x34, 0x7f, 0x1a, 0xaa, 0xe9, 0x27, 0x91, 0xd0, 0x7c, 0x23, 0x8d, 0x6e,
|
||||
0x00, 0x86, 0x4f, 0x23, 0x87, 0xdc, 0x53, 0x99, 0x99, 0x9b, 0xc9, 0xae, 0x50, 0xf1, 0x55, 0xd0,
|
||||
0x04, 0x89, 0xe2, 0x2d, 0x5a, 0x2a, 0x54, 0x89, 0xc7, 0x70, 0x48, 0x3e, 0xf1, 0xc8, 0x01, 0x87,
|
||||
0x67, 0x35, 0xdb, 0x0f, 0x08, 0x82, 0x62, 0x53, 0xb5, 0x7b, 0xcb, 0xc5, 0x60, 0x85, 0x56, 0xc0,
|
||||
0xda, 0xed, 0x1b, 0x06, 0x02, 0xfc, 0xa9, 0x55, 0x99, 0x71, 0x54, 0x88, 0xd7, 0x33, 0xb4, 0xce,
|
||||
0x85, 0x2f, 0x24, 0x82, 0x80, 0xb3, 0xca, 0x56, 0x33, 0x3b, 0x5a, 0x2a, 0xd5, 0xed, 0xbb, 0x6b,
|
||||
0xc9, 0xc1, 0x26, 0x93, 0x51, 0xe2, 0x01, 0x88, 0x39, 0xe4, 0xe7, 0x56, 0xd3, 0x0f, 0x5d, 0xe9,
|
||||
0xfd, 0xcd, 0xeb, 0x13, 0xd6, 0xa0, 0xe3, 0x6c, 0x57, 0x50, 0x09, 0xe8, 0xb2, 0xd4, 0x47, 0xd9,
|
||||
0x0c, 0x3e, 0xac, 0xee, 0x65, 0x53, 0x8d, 0x00, 0x95, 0x90, 0x58, 0x94, 0x32, 0x1a, 0x32, 0xe2,
|
||||
0xe2, 0xc9, 0x66, 0x6d, 0xc8, 0xf2, 0x1e, 0x70, 0xe5, 0xaa, 0xa6, 0x48, 0xad, 0x4a, 0xaf, 0x2a,
|
||||
0x97, 0x59, 0x5e, 0x79, 0xec, 0xdf, 0x1f, 0xe1, 0x37, 0xb5, 0x48, 0x6f, 0x0e, 0xab, 0xce, 0x29,
|
||||
0x32, 0x99, 0x8d, 0xe0, 0xc9, 0x73, 0xba, 0x76, 0xa1, 0x25, 0xd8, 0x98, 0x19, 0x67, 0x87, 0x24,
|
||||
0x00, 0xbb, 0x52, 0x15, 0x41, 0x28, 0x56, 0x1a, 0x6c, 0xb5, 0xbc, 0x4c, 0xcc, 0x17, 0x8a, 0xc7,
|
||||
0x1b, 0xc9, 0xea, 0x31, 0xbb, 0x68, 0x16, 0x5a, 0x72, 0x04, 0xbf, 0x9d, 0x3a, 0xb1, 0xc6, 0xe0,
|
||||
0x45, 0x51, 0x39, 0x03, 0x48, 0x82, 0xa6, 0x0c, 0x8a, 0xd2, 0x22, 0x30, 0xf3, 0x74, 0x4d, 0xd3,
|
||||
0x5c, 0x6c, 0x3e, 0x36, 0x90, 0xc5, 0xe6, 0xcf, 0xd3, 0xde, 0x68, 0x6a, 0x43, 0x5b, 0x2b, 0x90,
|
||||
0xbe, 0xa6, 0x23, 0xa0, 0x3b, 0x40, 0x6b, 0x99, 0x60, 0xdf, 0xff, 0x9f, 0x7c, 0x84, 0xc7, 0xf0,
|
||||
0xc7, 0x99, 0x1b, 0x99, 0x50, 0x06, 0x9b, 0xe7, 0x78, 0x2a, 0x4e, 0x76, 0x81, 0xea, 0x06, 0x29,
|
||||
0xab, 0xd8, 0xa6, 0x1e, 0x7f, 0x5a, 0x28, 0x0b, 0x90, 0x0e, 0x5d, 0x04, 0xf7, 0x57, 0x0a, 0x31,
|
||||
0x14, 0x2c, 0x84, 0x56, 0x57, 0x17, 0x8a, 0xa0, 0xd7, 0x7c, 0x3a, 0xf7, 0xda, 0x8d, 0x8a, 0xcb,
|
||||
0x47, 0xe8, 0xb2, 0xee, 0x32, 0x2a, 0xf8, 0x42, 0x42, 0xd8, 0x75, 0x8c, 0x47, 0x73, 0x2a, 0x87,
|
||||
0xe0, 0x0a, 0xe8, 0x6f, 0xed, 0xf2, 0x1a, 0x14, 0x3a, 0x59, 0xc7, 0x8e, 0x21, 0x72, 0xf7, 0x05,
|
||||
0x5b, 0xd1, 0x2b, 0x7d, 0x53, 0xfb, 0x22, 0xfb, 0x7e, 0xe7, 0x25, 0x0b, 0x1c, 0x15, 0xb2, 0xde,
|
||||
0xdf, 0x66, 0xcc, 0xe9, 0x30, 0xfc, 0x75, 0x8c, 0xa3, 0x09, 0xab, 0x42, 0x27, 0x6b, 0x33, 0xa0,
|
||||
0xdf, 0x15, 0xa4, 0x00, 0x10, 0x18, 0xe5, 0x10, 0x97, 0x47, 0xe9, 0x2e, 0x65, 0xd3, 0x9e, 0x44,
|
||||
0x1b, 0xaf, 0x36, 0x60, 0xcf, 0xe2, 0x1a, 0x6b, 0xbe, 0x7b, 0x1f, 0x80, 0xdd, 0x5c, 0x10, 0x5c,
|
||||
0x89, 0x23, 0xba, 0xa1, 0x98, 0xcc, 0x88, 0x74, 0xbf, 0x26, 0x4c, 0x0c, 0xa5, 0xc7, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35, 0x30, 0x39, 0x00, 0x00, 0x03, 0x7f, 0x30, 0x82, 0x03,
|
||||
0x7b, 0x30, 0x82, 0x02, 0x63, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x22, 0x12, 0x53, 0x46,
|
||||
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
|
||||
0x6d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13,
|
||||
0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72,
|
||||
0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x4d, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x10, 0x30, 0x0e, 0x06,
|
||||
0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30,
|
||||
0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74, 0x65, 0x72, 0x31,
|
||||
0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x44, 0x61, 0x73, 0x68, 0x30, 0x20,
|
||||
0x17, 0x0d, 0x32, 0x31, 0x30, 0x32, 0x30, 0x39, 0x31, 0x37, 0x31, 0x34, 0x32, 0x37, 0x5a, 0x18,
|
||||
0x0f, 0x32, 0x32, 0x39, 0x34, 0x31, 0x31, 0x32, 0x35, 0x31, 0x37, 0x31, 0x34, 0x32, 0x37, 0x5a,
|
||||
0x30, 0x6d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
|
||||
0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f,
|
||||
0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x4d,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x10, 0x30, 0x0e,
|
||||
0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74, 0x65, 0x72, 0x31, 0x10,
|
||||
0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74, 0x65, 0x72,
|
||||
0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x44, 0x61, 0x73, 0x68, 0x30,
|
||||
0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
|
||||
0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
|
||||
0x94, 0x7d, 0x0d, 0x32, 0x57, 0xf8, 0x90, 0x9b, 0x5a, 0xc5, 0x61, 0x48, 0xa8, 0xbb, 0x5e, 0x9d,
|
||||
0x4c, 0x4f, 0x53, 0xb9, 0x3b, 0x1f, 0x46, 0xc4, 0xd1, 0xba, 0x69, 0x7b, 0x71, 0x37, 0x20, 0xa1,
|
||||
0x44, 0x4f, 0xd1, 0x87, 0x71, 0x88, 0xc9, 0xe4, 0x49, 0xe1, 0xf0, 0x3d, 0x18, 0xca, 0xf7, 0x56,
|
||||
0xc4, 0x61, 0x4e, 0xa7, 0x9a, 0x93, 0x26, 0x8b, 0x03, 0x01, 0xa8, 0xef, 0x44, 0x89, 0xc6, 0x4d,
|
||||
0xab, 0x63, 0x92, 0xb2, 0xb5, 0xcd, 0x51, 0xb4, 0x12, 0x98, 0x2b, 0x89, 0x73, 0x28, 0xfa, 0x73,
|
||||
0xb2, 0xf4, 0xb7, 0xf2, 0x85, 0x3f, 0xe8, 0xf0, 0x38, 0x4f, 0x1d, 0xd0, 0x3b, 0xe7, 0xd3, 0xf0,
|
||||
0x22, 0xfd, 0x50, 0x11, 0x6d, 0x20, 0xe4, 0x8d, 0x87, 0xd7, 0x99, 0xd4, 0x70, 0x4e, 0xb9, 0xcb,
|
||||
0x5b, 0xbb, 0x4f, 0xd9, 0xa6, 0xf6, 0x51, 0x8b, 0x44, 0x5b, 0x9d, 0x68, 0x0b, 0x40, 0x2d, 0x11,
|
||||
0x18, 0xdc, 0xb6, 0x29, 0x73, 0xdb, 0x6a, 0x00, 0x12, 0xa0, 0x9f, 0xf4, 0xed, 0xeb, 0xa3, 0x4b,
|
||||
0x60, 0xdc, 0x51, 0xed, 0xbe, 0x6c, 0x70, 0x4b, 0x32, 0xda, 0xa0, 0x53, 0x15, 0xac, 0x5e, 0xfd,
|
||||
0x4d, 0x14, 0xd7, 0x75, 0xc8, 0x6f, 0x02, 0x85, 0x33, 0x95, 0xbe, 0x86, 0xee, 0x6d, 0x4d, 0x75,
|
||||
0x0f, 0x64, 0xfe, 0x9d, 0xa1, 0x3f, 0x53, 0x1b, 0xa1, 0xeb, 0x7b, 0xe6, 0xdd, 0xa1, 0x0a, 0x38,
|
||||
0xad, 0xce, 0xa3, 0x66, 0xda, 0x51, 0x38, 0xf3, 0x33, 0x3d, 0x96, 0x06, 0xa5, 0x0e, 0xa7, 0xfd,
|
||||
0xb2, 0x7b, 0xd5, 0x21, 0x1a, 0x35, 0x76, 0x25, 0x95, 0x97, 0xd6, 0xd2, 0xfe, 0xbd, 0x86, 0x22,
|
||||
0x05, 0xa3, 0x7d, 0x67, 0x60, 0x86, 0x04, 0xc3, 0xa5, 0xd3, 0xb7, 0x40, 0x0c, 0x31, 0x30, 0xc8,
|
||||
0x93, 0xb7, 0x61, 0xb3, 0x68, 0x90, 0x9d, 0xa0, 0x49, 0x79, 0x9b, 0xc1, 0x9c, 0x47, 0xa8, 0x81,
|
||||
0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x21, 0x30, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
|
||||
0x04, 0x16, 0x04, 0x14, 0x71, 0x50, 0x13, 0x2c, 0x3e, 0xaa, 0xfc, 0x7e, 0x6d, 0x16, 0x18, 0xc0,
|
||||
0x6f, 0x32, 0x2e, 0xf0, 0xe1, 0x03, 0x46, 0x94, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x24, 0x98, 0xc6, 0xd6,
|
||||
0xab, 0xb3, 0x94, 0xa1, 0x5d, 0x50, 0x0a, 0x82, 0xf7, 0x1d, 0xf5, 0xdf, 0x93, 0xf2, 0xf2, 0xf2,
|
||||
0xe6, 0x2a, 0x28, 0x67, 0x06, 0x36, 0xf6, 0x1f, 0x8c, 0xa3, 0x41, 0x43, 0x98, 0xc4, 0x6a, 0xd0,
|
||||
0x14, 0x0b, 0x1b, 0x89, 0x75, 0xb1, 0xe5, 0x79, 0x2c, 0xe8, 0xfd, 0x6d, 0x77, 0x72, 0xaa, 0x06,
|
||||
0x15, 0xd3, 0xca, 0x95, 0xca, 0x7d, 0xd5, 0xce, 0xca, 0x5f, 0x88, 0xd2, 0x3c, 0x08, 0xd2, 0x83,
|
||||
0xed, 0x7a, 0x0d, 0x2f, 0x34, 0x37, 0x97, 0x75, 0xd1, 0x2c, 0xa6, 0x30, 0x68, 0x8e, 0x19, 0x23,
|
||||
0x3d, 0x22, 0x62, 0x73, 0x4e, 0xd5, 0x42, 0x09, 0x82, 0xb6, 0x06, 0x17, 0xb8, 0xb6, 0x08, 0x64,
|
||||
0x73, 0x93, 0x02, 0x87, 0xd4, 0xf6, 0xa6, 0xce, 0xad, 0xfd, 0xcc, 0x9f, 0x69, 0x8b, 0xa8, 0xb3,
|
||||
0x4a, 0x45, 0x9e, 0xad, 0xa7, 0xf2, 0xb5, 0x91, 0xc8, 0x61, 0x48, 0x95, 0x8b, 0x36, 0x3e, 0x2f,
|
||||
0x40, 0x80, 0x69, 0xab, 0x3d, 0x45, 0xe1, 0x60, 0x3a, 0xe8, 0x33, 0x06, 0x12, 0x1a, 0x7e, 0x6e,
|
||||
0x11, 0x01, 0xb9, 0x66, 0x1b, 0x61, 0xbf, 0x01, 0x6d, 0x1d, 0x33, 0x58, 0x9a, 0xdd, 0x12, 0xf8,
|
||||
0xc1, 0xa3, 0x71, 0x89, 0x72, 0xed, 0xf4, 0xb2, 0xf3, 0x39, 0xc3, 0xf1, 0xf1, 0xe3, 0xe1, 0x9b,
|
||||
0xce, 0xc7, 0x83, 0x80, 0x32, 0x76, 0x16, 0x8c, 0x95, 0x35, 0xc0, 0xe8, 0xae, 0x02, 0x1b, 0x05,
|
||||
0x21, 0x36, 0xed, 0x4a, 0x71, 0xe0, 0x18, 0x76, 0xaa, 0xb9, 0x98, 0x07, 0x35, 0x27, 0x9b, 0xf2,
|
||||
0x5d, 0xdc, 0x79, 0xe6, 0xaa, 0x4a, 0x01, 0x23, 0x7d, 0x6d, 0x21, 0xbd, 0x97, 0x1b, 0x41, 0x60,
|
||||
0x7c, 0xb7, 0xfa, 0x21, 0x48, 0x52, 0x22, 0x94, 0x2d, 0xb0, 0xef, 0x2d, 0xc3, 0xe2, 0xe6, 0x37,
|
||||
0x55, 0x9d, 0xd9, 0x4d, 0xdd, 0xdd, 0x25, 0x25, 0x6b, 0x13, 0xb6, 0xb0, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x08, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x00, 0x00, 0x01, 0x77, 0x87, 0xc7,
|
||||
0x7b, 0x73, 0x00, 0x00, 0x02, 0xba, 0x30, 0x82, 0x02, 0xb6, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06,
|
||||
0x01, 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0xa2, 0x23, 0xce,
|
||||
0x04, 0x35, 0x63, 0x08, 0xa7, 0x0b, 0x42, 0x93, 0x53, 0x16, 0xa6, 0xbe, 0x54, 0xf2, 0xe4, 0x27,
|
||||
0x8d, 0xbb, 0x64, 0xe4, 0x23, 0xeb, 0x70, 0x6c, 0xd5, 0x28, 0xe7, 0x8b, 0x73, 0x68, 0xd0, 0x03,
|
||||
0xc5, 0x32, 0xfe, 0x4d, 0x80, 0xa5, 0xff, 0xcd, 0xf0, 0x5c, 0x31, 0xd1, 0xf0, 0xf1, 0xc7, 0x53,
|
||||
0xeb, 0xea, 0xa3, 0xb1, 0x07, 0xdc, 0x6b, 0x9a, 0xff, 0xcd, 0x36, 0x88, 0x83, 0x6c, 0x0a, 0xbf,
|
||||
0x85, 0x3a, 0x5d, 0xc4, 0x54, 0x0a, 0x63, 0xd6, 0xf1, 0x30, 0xa8, 0x18, 0x89, 0x6d, 0xba, 0x27,
|
||||
0x7f, 0xdc, 0xe1, 0x38, 0x6b, 0xa7, 0xc4, 0x2e, 0xdc, 0x21, 0x01, 0xf6, 0xdc, 0xc9, 0x36, 0x2a,
|
||||
0x6e, 0xb7, 0xbc, 0xdd, 0xe2, 0xd8, 0x44, 0x16, 0x9e, 0x28, 0x34, 0x9d, 0x59, 0x43, 0xc3, 0xe9,
|
||||
0x21, 0xb1, 0x46, 0x6b, 0x08, 0x81, 0x51, 0xa8, 0xa6, 0x84, 0xe1, 0xe3, 0x7d, 0x60, 0x6c, 0x3d,
|
||||
0xbc, 0x28, 0xea, 0xe7, 0x10, 0xd4, 0x07, 0xcc, 0x2e, 0xa4, 0xed, 0x66, 0xe6, 0xaa, 0xbf, 0xf5,
|
||||
0x95, 0xd9, 0xc9, 0xcd, 0x69, 0x44, 0xc6, 0x46, 0x66, 0xab, 0x99, 0xdb, 0x74, 0x9f, 0x7f, 0x4e,
|
||||
0x02, 0x37, 0x1e, 0x23, 0x52, 0x58, 0x10, 0x8c, 0x41, 0x6f, 0xe4, 0xc1, 0xca, 0x1e, 0xd0, 0xd3,
|
||||
0x8e, 0xd0, 0x36, 0xc6, 0xea, 0x61, 0x3d, 0x97, 0x35, 0x54, 0x81, 0xc4, 0x0e, 0x1a, 0x05, 0xa3,
|
||||
0x2b, 0x0b, 0x9e, 0xb2, 0x0d, 0xe9, 0xfc, 0x3a, 0xd9, 0x02, 0xce, 0x1b, 0x56, 0x92, 0x1a, 0x97,
|
||||
0x78, 0xda, 0xba, 0x40, 0x2c, 0x7c, 0x96, 0xe1, 0x94, 0x88, 0x1a, 0x7b, 0x47, 0x77, 0x59, 0xcf,
|
||||
0x98, 0x52, 0xb0, 0xfb, 0xcb, 0xb5, 0xf5, 0xe4, 0x16, 0x38, 0xb6, 0x05, 0x20, 0x5c, 0x36, 0x19,
|
||||
0xc5, 0xa9, 0x70, 0xe6, 0x89, 0x9f, 0x76, 0x3d, 0x88, 0x78, 0x56, 0xf2, 0x85, 0xff, 0x89, 0xc3,
|
||||
0xc5, 0x32, 0xd3, 0xb0, 0x8f, 0x1c, 0xc3, 0x23, 0xb4, 0x70, 0xd4, 0x8b, 0xd3, 0x6c, 0x27, 0xd1,
|
||||
0xc4, 0xe9, 0x0a, 0xaa, 0x06, 0x3a, 0xd3, 0xd6, 0x20, 0xe1, 0x08, 0x65, 0x10, 0x50, 0x45, 0x59,
|
||||
0xa5, 0xd4, 0xb2, 0xd5, 0xcb, 0x74, 0x52, 0x05, 0x95, 0x08, 0xe4, 0xb9, 0xe9, 0xc9, 0x7f, 0xbc,
|
||||
0x4b, 0x3f, 0xfa, 0x00, 0x5c, 0x20, 0xda, 0x8e, 0xb2, 0xe0, 0x4e, 0x51, 0x0e, 0xfe, 0x98, 0xd8,
|
||||
0xe9, 0xd5, 0x44, 0x0b, 0x1c, 0x1f, 0xe3, 0xa6, 0xc5, 0x03, 0xb1, 0x5e, 0x23, 0x7a, 0x0a, 0x1e,
|
||||
0xa1, 0x49, 0xaf, 0xea, 0xb8, 0xea, 0x74, 0x64, 0x05, 0xda, 0xad, 0xb3, 0x5f, 0x17, 0xa9, 0x9a,
|
||||
0x89, 0x16, 0x6d, 0xd2, 0xef, 0xa1, 0x35, 0x40, 0x43, 0x71, 0xee, 0x6c, 0x0c, 0x99, 0xbf, 0xcf,
|
||||
0xd5, 0xae, 0xad, 0x83, 0xeb, 0x60, 0x8d, 0x4e, 0xae, 0xe2, 0x01, 0xcb, 0xbe, 0x18, 0x94, 0x39,
|
||||
0xdf, 0xcb, 0xae, 0x47, 0xf7, 0x89, 0x9c, 0x37, 0x35, 0x43, 0x9b, 0xb4, 0x6c, 0xb9, 0x86, 0xc5,
|
||||
0xd6, 0x39, 0xd3, 0x47, 0x1f, 0x91, 0x8d, 0xe8, 0xd3, 0x13, 0xe7, 0xe2, 0x2c, 0x27, 0x36, 0xd9,
|
||||
0xc8, 0xf4, 0xc6, 0x25, 0x78, 0x02, 0x78, 0x31, 0x7f, 0x8a, 0xa1, 0x32, 0x50, 0x4d, 0xc1, 0x49,
|
||||
0x23, 0x8c, 0x72, 0x09, 0xc8, 0xe3, 0x7e, 0xa0, 0xdd, 0x1b, 0x96, 0x47, 0x24, 0xab, 0xb4, 0x14,
|
||||
0x6d, 0x07, 0x8e, 0x90, 0x29, 0x6c, 0xb2, 0x13, 0xe4, 0xe1, 0x69, 0x17, 0xfd, 0xb8, 0x9e, 0x52,
|
||||
0x90, 0x19, 0xbe, 0xf6, 0xd7, 0x60, 0x0e, 0x22, 0xb5, 0x01, 0x45, 0xab, 0xf5, 0xe4, 0x2e, 0x53,
|
||||
0x3f, 0xf0, 0x58, 0xbb, 0x2c, 0xa5, 0x31, 0x59, 0x4e, 0x4b, 0xf0, 0x3e, 0x36, 0x77, 0xe0, 0x05,
|
||||
0x38, 0x81, 0x07, 0xf6, 0x53, 0xf3, 0xff, 0x0c, 0x8d, 0x6d, 0xbc, 0xa9, 0x6f, 0x6a, 0x75, 0xef,
|
||||
0x99, 0x01, 0xc9, 0xd6, 0x4d, 0xa4, 0x9b, 0x35, 0x95, 0xe3, 0x20, 0xfd, 0x13, 0x51, 0x71, 0xbb,
|
||||
0xbd, 0x93, 0xc4, 0x8b, 0x98, 0x6c, 0x8c, 0x6a, 0x30, 0xea, 0x3e, 0xe7, 0x9b, 0x98, 0x10, 0xab,
|
||||
0x03, 0x3a, 0x90, 0xf4, 0x98, 0xf1, 0x30, 0xa5, 0xd6, 0x3a, 0x06, 0x62, 0xc0, 0x39, 0xf7, 0x36,
|
||||
0xa5, 0x66, 0xfd, 0x3d, 0x30, 0x51, 0x7a, 0x4d, 0x06, 0xf9, 0xfe, 0xa5, 0xda, 0xa8, 0x80, 0xaa,
|
||||
0x50, 0x5d, 0xff, 0xf2, 0x23, 0x6f, 0x1a, 0x7d, 0x63, 0xa6, 0x6a, 0x64, 0x5c, 0x2a, 0xeb, 0x83,
|
||||
0x42, 0x29, 0x5e, 0x26, 0x9d, 0x25, 0x0f, 0x58, 0xb6, 0x3b, 0x48, 0x66, 0xb0, 0x1f, 0x40, 0x07,
|
||||
0x85, 0xd4, 0xc3, 0x61, 0x7a, 0xa5, 0x6b, 0xa1, 0x61, 0x09, 0x78, 0x03, 0x58, 0xfa, 0x52, 0xde,
|
||||
0xb4, 0xf2, 0xc7, 0x40, 0x94, 0x7b, 0x6c, 0x4f, 0xec, 0x5f, 0x17, 0xdf, 0x97, 0xd2, 0x95, 0xa6,
|
||||
0x94, 0x8b, 0xb7, 0xcf, 0x05, 0x3d, 0x9d, 0xcc, 0x55, 0x1e, 0x83, 0x79, 0xf9, 0xe6, 0x22, 0x8e,
|
||||
0xdd, 0x20, 0x22, 0x87, 0x3b, 0xb6, 0x79, 0xc9, 0xcf, 0x4c, 0x8f, 0xbb, 0x4e, 0x1f, 0xd6, 0xa5,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35, 0x30, 0x39, 0x00, 0x00, 0x02, 0x7a, 0x30,
|
||||
0x82, 0x02, 0x76, 0x30, 0x82, 0x01, 0xdf, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x44, 0xbb,
|
||||
0xd2, 0x52, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
|
||||
0x00, 0x30, 0x6d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
|
||||
0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66,
|
||||
0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d,
|
||||
0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x10, 0x30,
|
||||
0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74, 0x65, 0x72, 0x31,
|
||||
0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74, 0x65,
|
||||
0x72, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x44, 0x61, 0x73, 0x68,
|
||||
0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32, 0x30, 0x39, 0x31, 0x37, 0x31, 0x32, 0x31, 0x30,
|
||||
0x5a, 0x18, 0x0f, 0x34, 0x37, 0x35, 0x39, 0x30, 0x31, 0x30, 0x37, 0x31, 0x37, 0x31, 0x32, 0x31,
|
||||
0x30, 0x5a, 0x30, 0x6d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
|
||||
0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69,
|
||||
0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
|
||||
0x0d, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x10,
|
||||
0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74, 0x65, 0x72,
|
||||
0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x46, 0x6c, 0x75, 0x74, 0x74,
|
||||
0x65, 0x72, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x44, 0x61, 0x73,
|
||||
0x68, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
|
||||
0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xce, 0x0c,
|
||||
0xe2, 0x0c, 0xc5, 0xb8, 0x20, 0x93, 0xf7, 0x7a, 0x38, 0x3c, 0xb5, 0x52, 0x1c, 0x48, 0x1a, 0x44,
|
||||
0xba, 0xca, 0x88, 0xa0, 0xf0, 0xd6, 0xad, 0x91, 0x9e, 0x0c, 0x09, 0x64, 0xdd, 0x2d, 0x84, 0x0f,
|
||||
0x9e, 0xcb, 0xbb, 0xd9, 0x24, 0xd6, 0xd6, 0xaa, 0x63, 0x1c, 0xfa, 0xb6, 0x45, 0x88, 0xf4, 0x8e,
|
||||
0xb9, 0x2e, 0xe9, 0xa9, 0xed, 0x6b, 0xda, 0xc6, 0x6b, 0x91, 0x06, 0xf9, 0x0a, 0x71, 0x42, 0x2e,
|
||||
0x18, 0x97, 0x80, 0x0c, 0x84, 0xea, 0x69, 0x8f, 0xc0, 0xb0, 0xa8, 0x76, 0xfc, 0x31, 0x86, 0xf9,
|
||||
0x09, 0x7e, 0xa4, 0xff, 0x24, 0x94, 0x79, 0x29, 0xca, 0xd0, 0x9a, 0x07, 0xf3, 0x25, 0x21, 0xa7,
|
||||
0x61, 0x6e, 0x81, 0x1c, 0x13, 0x02, 0xc9, 0xec, 0xa4, 0x42, 0xcc, 0x6c, 0x95, 0x01, 0xe5, 0x51,
|
||||
0x8e, 0x4a, 0xb3, 0x0b, 0x29, 0xf1, 0x8a, 0x1c, 0xb5, 0xe1, 0x41, 0xb6, 0xe3, 0x3d, 0x02, 0x03,
|
||||
0x01, 0x00, 0x01, 0xa3, 0x21, 0x30, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
|
||||
0x04, 0x14, 0x5f, 0xf5, 0x0a, 0x1f, 0xe7, 0xf5, 0xde, 0xbd, 0x7c, 0x59, 0xdd, 0x94, 0x26, 0x39,
|
||||
0xf5, 0xc9, 0x21, 0x88, 0xbf, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
|
||||
0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x17, 0x3d, 0x26, 0x25, 0xfb, 0xd1, 0x5b,
|
||||
0x9b, 0xd5, 0xed, 0x84, 0x4f, 0x06, 0xa3, 0x4c, 0x17, 0x6d, 0x91, 0xa9, 0x19, 0xc8, 0xa2, 0x1a,
|
||||
0x65, 0x4f, 0xf3, 0x23, 0x28, 0x18, 0x46, 0xe3, 0x77, 0x78, 0xae, 0x2c, 0x5a, 0xfa, 0x1a, 0x01,
|
||||
0x3b, 0x92, 0xa6, 0x7c, 0xee, 0x3c, 0xa0, 0x4c, 0x9e, 0xb1, 0x26, 0xed, 0x9e, 0x1b, 0x81, 0x40,
|
||||
0xa5, 0xce, 0xa8, 0xad, 0x09, 0xdd, 0x7c, 0x83, 0xc8, 0x1d, 0x1e, 0x6a, 0x3e, 0x60, 0x2e, 0x95,
|
||||
0xc6, 0x17, 0x5b, 0x88, 0x0b, 0x54, 0x48, 0x80, 0x95, 0x77, 0x78, 0xcc, 0x5e, 0x09, 0x9e, 0x66,
|
||||
0xe5, 0x87, 0x64, 0x4d, 0x36, 0x12, 0x40, 0xc4, 0x67, 0x78, 0xce, 0x38, 0x60, 0x24, 0xdf, 0x3c,
|
||||
0xc0, 0xbb, 0xf7, 0x7d, 0x2f, 0x66, 0x56, 0xfb, 0xfa, 0x75, 0x2a, 0xe5, 0x23, 0x7a, 0xad, 0x5c,
|
||||
0xef, 0x2d, 0xa1, 0xb6, 0x7c, 0xbd, 0xfa, 0xb3, 0xdc, 0x68, 0x55, 0xd1, 0xa0, 0xac, 0x8c, 0x06,
|
||||
0x62, 0x21, 0xe9, 0x7d, 0x64, 0xd0, 0x60, 0xb3, 0x12, 0x2e, 0x6a, 0x50, 0xf4
|
||||
];
|
||||
|
||||
@override
|
||||
String get appManifest => r'''
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.splitaot">
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
In most cases you can leave this as-is, but you if you want to provide
|
||||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
|
||||
android:label="splitaot"
|
||||
android:extractNativeLibs="false">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping"
|
||||
android:value="2:component1" />
|
||||
</application>
|
||||
</manifest>
|
||||
''';
|
||||
|
||||
@override
|
||||
String get appStrings => r'''
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="component1Name">component1</string>
|
||||
</resources>
|
||||
''';
|
||||
|
||||
@override
|
||||
String get appStyles => r'''
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">@android:color/white</item>
|
||||
</style>
|
||||
</resources>
|
||||
''';
|
||||
|
||||
@override
|
||||
String get appLaunchBackground => r'''
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
''';
|
||||
|
||||
@override
|
||||
String get asset1 => r'''
|
||||
asset 1 contents
|
||||
''';
|
||||
|
||||
@override
|
||||
String get asset2 => r'''
|
||||
asset 2 contents
|
||||
''';
|
||||
|
||||
@override
|
||||
List<DeferredComponentModule> get deferredComponents => <DeferredComponentModule>[DeferredComponentModule('component1')];
|
||||
}
|
||||
|
||||
/// Missing android dynamic feature module.
|
||||
class NoAndroidDynamicFeatureModuleDeferredComponentsConfig extends BasicDeferredComponentsConfig {
|
||||
@override
|
||||
List<DeferredComponentModule> get deferredComponents => <DeferredComponentModule>[];
|
||||
}
|
||||
|
||||
/// Missing golden
|
||||
class NoGoldenDeferredComponentsConfig extends BasicDeferredComponentsConfig {
|
||||
@override
|
||||
String get deferredComponentsGolden => null;
|
||||
}
|
||||
|
||||
/// Missing golden
|
||||
class MismatchedGoldenDeferredComponentsConfig extends BasicDeferredComponentsConfig {
|
||||
@override
|
||||
String get deferredComponentsGolden => r'''
|
||||
loading-units:
|
||||
- id: 2
|
||||
libraries:
|
||||
- package:test/invalid_lib_name.dart
|
||||
''';
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
import 'package:file/file.dart';
|
||||
|
||||
import '../test_utils.dart';
|
||||
import 'deferred_components_config.dart';
|
||||
|
||||
const String _kDefaultHtml = '''
|
||||
<html>
|
||||
@ -26,6 +27,7 @@ abstract class Project {
|
||||
String get main;
|
||||
String get test => null;
|
||||
String get generatedFile => null;
|
||||
DeferredComponentsConfig get deferredComponents => null;
|
||||
|
||||
Uri get mainDart => Uri.parse('package:test/main.dart');
|
||||
|
||||
@ -41,6 +43,9 @@ abstract class Project {
|
||||
if (generatedFile != null) {
|
||||
writeFile(fileSystem.path.join(dir.path, '.dart_tool', 'flutter_gen', 'flutter_gen.dart'), generatedFile);
|
||||
}
|
||||
if (deferredComponents != null) {
|
||||
deferredComponents.setUpIn(dir);
|
||||
}
|
||||
writeFile(fileSystem.path.join(dir.path, 'web', 'index.html'), _kDefaultHtml);
|
||||
writePackages(dir.path);
|
||||
await getPackages(dir.path);
|
||||
|
@ -40,6 +40,13 @@ void writeFile(String path, String content) {
|
||||
..setLastModifiedSync(DateTime.now().add(const Duration(seconds: 10)));
|
||||
}
|
||||
|
||||
void writeBytesFile(String path, List<int> content) {
|
||||
fileSystem.file(path)
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(content)
|
||||
..setLastModifiedSync(DateTime.now().add(const Duration(seconds: 10)));
|
||||
}
|
||||
|
||||
void writePackages(String folder) {
|
||||
writeFile(fileSystem.path.join(folder, '.packages'), '''
|
||||
test:${fileSystem.path.join(fileSystem.currentDirectory.path, 'lib')}/
|
||||
|
@ -35,6 +35,8 @@ class FakeAndroidBuilder implements AndroidBuilder {
|
||||
@required FlutterProject project,
|
||||
@required AndroidBuildInfo androidBuildInfo,
|
||||
@required String target,
|
||||
bool validateDeferredComponents = true,
|
||||
bool deferredComponentsEnabled = false,
|
||||
}) async {}
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,15 @@ String getFlutterRoot() {
|
||||
return path.normalize(path.join(toolsPath, '..', '..'));
|
||||
}
|
||||
|
||||
/// Gets the path to the root of the Android SDK from the environment variable.
|
||||
String getAndroidSdkRoot() {
|
||||
const Platform platform = LocalPlatform();
|
||||
if (platform.environment.containsKey('ANDROID_SDK_ROOT')) {
|
||||
return platform.environment['ANDROID_SDK_ROOT'];
|
||||
}
|
||||
throw StateError('ANDROID_SDK_ROOT environment varible not set');
|
||||
}
|
||||
|
||||
CommandRunner<void> createTestCommandRunner([ FlutterCommand command ]) {
|
||||
final FlutterCommandRunner runner = TestFlutterCommandRunner();
|
||||
if (command != null) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user