Unverified Commit 49306139 authored by chunhtai's avatar chunhtai Committed by GitHub

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)
parent 6fd33879
......@@ -16,6 +16,7 @@ import '../runner/flutter_command.dart';
import 'analyze_base.dart';
import 'analyze_continuously.dart';
import 'analyze_once.dart';
import 'android_analyze.dart';
import 'validate_project.dart';
class AnalyzeCommand extends FlutterCommand {
......@@ -99,6 +100,37 @@ class AnalyzeCommand extends FlutterCommand {
argParser.addFlag('fatal-warnings',
help: 'Treat warning level issues as fatal.',
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.
......@@ -142,12 +174,51 @@ class AnalyzeCommand extends FlutterCommand {
return false;
}
// Don't run pub if asking for android analysis.
if (boolArg('android')) {
return false;
}
return super.shouldRunPub;
}
@override
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;
if (boolArg('watch')) {
throwToolExit('flag --watch is not compatible with --suggestions');
......
// 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');
}
}
}
// 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;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment