Add android analyzer commands for applinks (#131009)
Since applink validation tool is going to be a static tool, It won't have access to vmservices. [flutter.dev/go/static-tooling-in-devtools](http://flutter.dev/go/static-tooling-in-devtools) I remove the vm services and also update the deeplink task to also include path pattern and custom scheme http://go/android-applink-apis (internal only)
This commit is contained in:
parent
6fd3387977
commit
4930613999
@ -16,6 +16,7 @@ import '../runner/flutter_command.dart';
|
|||||||
import 'analyze_base.dart';
|
import 'analyze_base.dart';
|
||||||
import 'analyze_continuously.dart';
|
import 'analyze_continuously.dart';
|
||||||
import 'analyze_once.dart';
|
import 'analyze_once.dart';
|
||||||
|
import 'android_analyze.dart';
|
||||||
import 'validate_project.dart';
|
import 'validate_project.dart';
|
||||||
|
|
||||||
class AnalyzeCommand extends FlutterCommand {
|
class AnalyzeCommand extends FlutterCommand {
|
||||||
@ -99,6 +100,37 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
argParser.addFlag('fatal-warnings',
|
argParser.addFlag('fatal-warnings',
|
||||||
help: 'Treat warning level issues as fatal.',
|
help: 'Treat warning level issues as fatal.',
|
||||||
defaultsTo: true);
|
defaultsTo: true);
|
||||||
|
|
||||||
|
argParser.addFlag('android',
|
||||||
|
negatable: false,
|
||||||
|
help: 'Analyze Android sub-project. Used by internal tools only.',
|
||||||
|
hide: !verboseHelp,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (verboseHelp) {
|
||||||
|
argParser.addSeparator('Usage: flutter analyze --android [arguments]');
|
||||||
|
}
|
||||||
|
|
||||||
|
argParser.addFlag('list-build-variants',
|
||||||
|
negatable: false,
|
||||||
|
help: 'Print out a list of available build variants for the '
|
||||||
|
'Android sub-project.',
|
||||||
|
hide: !verboseHelp,
|
||||||
|
);
|
||||||
|
|
||||||
|
argParser.addFlag('output-app-link-settings',
|
||||||
|
negatable: false,
|
||||||
|
help: 'Output a JSON with Android app link settings into a file. '
|
||||||
|
'The "--build-variant" must also be set.',
|
||||||
|
hide: !verboseHelp,
|
||||||
|
);
|
||||||
|
|
||||||
|
argParser.addOption('build-variant',
|
||||||
|
help: 'Sets the Android build variant to be analyzed.',
|
||||||
|
valueHelp: 'use "flutter analyze --android --list-build-variants" to get '
|
||||||
|
'all available build variants',
|
||||||
|
hide: !verboseHelp,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The working directory for testing analysis using dartanalyzer.
|
/// The working directory for testing analysis using dartanalyzer.
|
||||||
@ -142,12 +174,51 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't run pub if asking for android analysis.
|
||||||
|
if (boolArg('android')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return super.shouldRunPub;
|
return super.shouldRunPub;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<FlutterCommandResult> runCommand() async {
|
Future<FlutterCommandResult> runCommand() async {
|
||||||
if (boolArg('suggestions')) {
|
if (boolArg('android')) {
|
||||||
|
final AndroidAnalyzeOption option;
|
||||||
|
final String? buildVariant;
|
||||||
|
if (argResults!['list-build-variants'] as bool && argResults!['output-app-link-settings'] as bool) {
|
||||||
|
throwToolExit('Only one of "--list-build-variants" or "--output-app-link-settings" can be provided');
|
||||||
|
}
|
||||||
|
if (argResults!['list-build-variants'] as bool) {
|
||||||
|
option = AndroidAnalyzeOption.listBuildVariant;
|
||||||
|
buildVariant = null;
|
||||||
|
} else if (argResults!['output-app-link-settings'] as bool) {
|
||||||
|
option = AndroidAnalyzeOption.outputAppLinkSettings;
|
||||||
|
buildVariant = argResults!['build-variant'] as String?;
|
||||||
|
if (buildVariant == null) {
|
||||||
|
throwToolExit('"--build-variant" must be provided');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throwToolExit('No argument is provided to analyze. Use -h to see available commands.');
|
||||||
|
}
|
||||||
|
final Set<String> items = findDirectories(argResults!, _fileSystem);
|
||||||
|
final String directoryPath;
|
||||||
|
if (items.isEmpty) { // user did not specify any path
|
||||||
|
directoryPath = _fileSystem.currentDirectory.path;
|
||||||
|
} else if (items.length > 1) { // if the user sends more than one path
|
||||||
|
throwToolExit('The Android analyze can process only one directory path');
|
||||||
|
} else {
|
||||||
|
directoryPath = items.first;
|
||||||
|
}
|
||||||
|
await AndroidAnalyze(
|
||||||
|
fileSystem: _fileSystem,
|
||||||
|
option: option,
|
||||||
|
userPath: directoryPath,
|
||||||
|
buildVariant: buildVariant,
|
||||||
|
logger: _logger,
|
||||||
|
).analyze();
|
||||||
|
} else if (boolArg('suggestions')) {
|
||||||
final String directoryPath;
|
final String directoryPath;
|
||||||
if (boolArg('watch')) {
|
if (boolArg('watch')) {
|
||||||
throwToolExit('flag --watch is not compatible with --suggestions');
|
throwToolExit('flag --watch is not compatible with --suggestions');
|
||||||
|
56
packages/flutter_tools/lib/src/commands/android_analyze.dart
Normal file
56
packages/flutter_tools/lib/src/commands/android_analyze.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import '../base/file_system.dart';
|
||||||
|
import '../base/logger.dart';
|
||||||
|
import '../convert.dart';
|
||||||
|
import '../project.dart';
|
||||||
|
|
||||||
|
/// The type of analysis to perform.
|
||||||
|
enum AndroidAnalyzeOption {
|
||||||
|
/// Prints out available build variants of the Android sub-project.
|
||||||
|
///
|
||||||
|
/// An example output:
|
||||||
|
/// ["debug", "profile", "release"]
|
||||||
|
listBuildVariant,
|
||||||
|
|
||||||
|
/// Outputs app link settings of the Android sub-project into a file.
|
||||||
|
///
|
||||||
|
/// The file path will be printed after the command is run successfully.
|
||||||
|
outputAppLinkSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Analyze the Android sub-project of a Flutter project.
|
||||||
|
///
|
||||||
|
/// The [userPath] must be point to a flutter project.
|
||||||
|
class AndroidAnalyze {
|
||||||
|
AndroidAnalyze({
|
||||||
|
required this.fileSystem,
|
||||||
|
required this.option,
|
||||||
|
required this.userPath,
|
||||||
|
this.buildVariant,
|
||||||
|
required this.logger,
|
||||||
|
}) : assert(option == AndroidAnalyzeOption.listBuildVariant || buildVariant != null);
|
||||||
|
|
||||||
|
final FileSystem fileSystem;
|
||||||
|
final AndroidAnalyzeOption option;
|
||||||
|
final String? buildVariant;
|
||||||
|
final String userPath;
|
||||||
|
final Logger logger;
|
||||||
|
|
||||||
|
Future<void> analyze() async {
|
||||||
|
final FlutterProject project = FlutterProject.fromDirectory(fileSystem.directory(userPath));
|
||||||
|
switch (option) {
|
||||||
|
case AndroidAnalyzeOption.listBuildVariant:
|
||||||
|
logger.printStatus(jsonEncode(await project.android.getBuildVariants()));
|
||||||
|
case AndroidAnalyzeOption.outputAppLinkSettings:
|
||||||
|
assert(buildVariant != null);
|
||||||
|
await project.android.outputsAppLinkSettings(variant: buildVariant!);
|
||||||
|
final String filePath = fileSystem.path.join(project.directory.path, 'build', 'app', 'app-link-settings-$buildVariant.json`');
|
||||||
|
logger.printStatus('result saved in $filePath');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:file/file.dart';
|
||||||
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:flutter_tools/src/android/android_builder.dart';
|
||||||
|
import 'package:flutter_tools/src/artifacts.dart';
|
||||||
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
|
import 'package:flutter_tools/src/base/terminal.dart';
|
||||||
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
|
import 'package:flutter_tools/src/commands/analyze.dart';
|
||||||
|
import 'package:flutter_tools/src/project.dart';
|
||||||
|
import 'package:flutter_tools/src/project_validator.dart';
|
||||||
|
import 'package:test/fake.dart';
|
||||||
|
|
||||||
|
import '../../src/context.dart';
|
||||||
|
import '../../src/test_flutter_command_runner.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
group('Android analyze command', () {
|
||||||
|
late FileSystem fileSystem;
|
||||||
|
late Platform platform;
|
||||||
|
late BufferLogger logger;
|
||||||
|
late FakeProcessManager processManager;
|
||||||
|
late Terminal terminal;
|
||||||
|
late AnalyzeCommand command;
|
||||||
|
late CommandRunner<void> runner;
|
||||||
|
late Directory tempDir;
|
||||||
|
late FakeAndroidBuilder builder;
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
Cache.disableLocking();
|
||||||
|
});
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
fileSystem = MemoryFileSystem.test();
|
||||||
|
platform = FakePlatform();
|
||||||
|
logger = BufferLogger.test();
|
||||||
|
processManager = FakeProcessManager.empty();
|
||||||
|
terminal = Terminal.test();
|
||||||
|
command = AnalyzeCommand(
|
||||||
|
artifacts: Artifacts.test(),
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
logger: logger,
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
terminal: terminal,
|
||||||
|
allProjectValidators: <ProjectValidator>[],
|
||||||
|
suppressAnalytics: true,
|
||||||
|
);
|
||||||
|
runner = createTestCommandRunner(command);
|
||||||
|
tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
|
||||||
|
tempDir.childDirectory('android').createSync();
|
||||||
|
|
||||||
|
// Setup repo roots
|
||||||
|
const String homePath = '/home/user/flutter';
|
||||||
|
Cache.flutterRoot = homePath;
|
||||||
|
for (final String dir in <String>['dev', 'examples', 'packages']) {
|
||||||
|
fileSystem.directory(homePath).childDirectory(dir).createSync(recursive: true);
|
||||||
|
}
|
||||||
|
builder = FakeAndroidBuilder();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('can list build variants', () async {
|
||||||
|
builder.variants = <String>['debug', 'release'];
|
||||||
|
await runner.run(<String>['analyze', '--android', '--list-build-variants', tempDir.path]);
|
||||||
|
expect(logger.statusText, contains('["debug","release"]'));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
AndroidBuilder: () => builder,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('throw if provide multiple path', () async {
|
||||||
|
final Directory anotherTempDir = fileSystem.systemTempDirectory.createTempSync('another');
|
||||||
|
await expectLater(
|
||||||
|
runner.run(<String>['analyze', '--android', '--list-build-variants', tempDir.path, anotherTempDir.path]),
|
||||||
|
throwsA(
|
||||||
|
isA<Exception>().having(
|
||||||
|
(Exception e) => e.toString(),
|
||||||
|
'description',
|
||||||
|
contains('The Android analyze can process only one directory path'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('can output app link settings', () async {
|
||||||
|
const String buildVariant = 'release';
|
||||||
|
await runner.run(<String>['analyze', '--android', '--output-app-link-settings', '--build-variant=$buildVariant', tempDir.path]);
|
||||||
|
expect(builder.outputVariant, buildVariant);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
AndroidBuilder: () => builder,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('output app link settings throws if no build variant', () async {
|
||||||
|
await expectLater(
|
||||||
|
runner.run(<String>['analyze', '--android', '--output-app-link-settings', tempDir.path]),
|
||||||
|
throwsA(
|
||||||
|
isA<Exception>().having(
|
||||||
|
(Exception e) => e.toString(),
|
||||||
|
'description',
|
||||||
|
contains('"--build-variant" must be provided'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeAndroidBuilder extends Fake implements AndroidBuilder {
|
||||||
|
List<String> variants = const <String>[];
|
||||||
|
String? outputVariant;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> getBuildVariants({required FlutterProject project}) async {
|
||||||
|
return variants;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> outputsAppLinkSettings(String buildVariant, {required FlutterProject project}) async {
|
||||||
|
outputVariant = buildVariant;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user