Unverified Commit bb8cf609 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Build local maven repo when using local engine (#44243)

parent 3e4bf575
...@@ -73,14 +73,6 @@ flutter { ...@@ -73,14 +73,6 @@ flutter {
} }
dependencies { dependencies {
// TODO(egarciad): Remove these dependencies once https://github.com/flutter/flutter/issues/40866 is fixed.
implementation "androidx.annotation:annotation:1.1.0"
def lifecycle_version = "2.1.0"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
implementation "androidx.fragment:fragment:1.1.0"
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:rules:1.1.1' androidTestImplementation 'androidx.test:rules:1.1.1'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.1.1'
......
...@@ -96,7 +96,6 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -96,7 +96,6 @@ class FlutterPlugin implements Plugin<Project> {
private String localEngine private String localEngine
private String localEngineSrcPath private String localEngineSrcPath
private Properties localProperties private Properties localProperties
private File flutterJar
private String engineVersion private String engineVersion
@Override @Override
...@@ -144,8 +143,9 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -144,8 +143,9 @@ class FlutterPlugin implements Plugin<Project> {
throw new GradleException("flutter.sdk must point to the Flutter SDK directory") throw new GradleException("flutter.sdk must point to the Flutter SDK directory")
} }
engineVersion = Paths.get(flutterRoot.absolutePath, "bin", "internal", "engine.version") engineVersion = useLocalEngine()
.toFile().text.trim() ? "+" // Match any version since there's only one.
: "1.0.0-" + Paths.get(flutterRoot.absolutePath, "bin", "internal", "engine.version").toFile().text.trim()
String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter" String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"
flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile(); flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile();
...@@ -178,32 +178,19 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -178,32 +178,19 @@ class FlutterPlugin implements Plugin<Project> {
} }
} }
} }
if (useLocalEngine()) { if (useLocalEngine()) {
String engineOutPath = project.property('localEngineOut') // This is required to pass the local engine to flutter build aot.
String engineOutPath = project.property('local-engine-out')
File engineOut = project.file(engineOutPath) File engineOut = project.file(engineOutPath)
if (!engineOut.isDirectory()) { if (!engineOut.isDirectory()) {
throw new GradleException('localEngineOut must point to a local engine build') throw new GradleException('local-engine-out must point to a local engine build')
}
Path baseEnginePath = Paths.get(engineOut.absolutePath)
flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
if (!flutterJar.isFile()) {
throw new GradleException("Local engine jar not found: $flutterJar")
} }
localEngine = engineOut.name localEngine = engineOut.name
localEngineSrcPath = engineOut.parentFile.parent localEngineSrcPath = engineOut.parentFile.parent
// The local engine is built for one of the build type.
// However, we use the same engine for each of the build types.
project.android.buildTypes.each {
addApiDependencies(project, it.name, project.files {
flutterJar
})
} }
} else {
project.android.buildTypes.each this.&addFlutterDependencies project.android.buildTypes.each this.&addFlutterDependencies
project.android.buildTypes.whenObjectAdded this.&addFlutterDependencies project.android.buildTypes.whenObjectAdded this.&addFlutterDependencies
} }
}
/** /**
* Adds the dependencies required by the Flutter project. * Adds the dependencies required by the Flutter project.
...@@ -212,17 +199,24 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -212,17 +199,24 @@ class FlutterPlugin implements Plugin<Project> {
* 2. libflutter.so * 2. libflutter.so
*/ */
void addFlutterDependencies(buildType) { void addFlutterDependencies(buildType) {
String flutterBuildMode = buildModeFor(buildType)
if (!supportsBuildMode(flutterBuildMode)) {
return
}
String repository = useLocalEngine()
? project.property('local-engine-repo')
: MAVEN_REPO
project.rootProject.allprojects { project.rootProject.allprojects {
repositories { repositories {
maven { maven {
url MAVEN_REPO url repository
} }
} }
} }
String flutterBuildMode = buildModeFor(buildType)
// Add the embedding dependency. // Add the embedding dependency.
addApiDependencies(project, buildType.name, addApiDependencies(project, buildType.name,
"io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion") "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion")
List<String> platforms = getTargetPlatforms().collect() List<String> platforms = getTargetPlatforms().collect()
// Debug mode includes x86 and x64, which are commonly used in emulators. // Debug mode includes x86 and x64, which are commonly used in emulators.
...@@ -234,7 +228,7 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -234,7 +228,7 @@ class FlutterPlugin implements Plugin<Project> {
String arch = PLATFORM_ARCH_MAP[platform].replace("-", "_") String arch = PLATFORM_ARCH_MAP[platform].replace("-", "_")
// Add the `libflutter.so` dependency. // Add the `libflutter.so` dependency.
addApiDependencies(project, buildType.name, addApiDependencies(project, buildType.name,
"io.flutter:${arch}_$flutterBuildMode:1.0.0-$engineVersion") "io.flutter:${arch}_$flutterBuildMode:$engineVersion")
} }
} }
...@@ -261,9 +255,6 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -261,9 +255,6 @@ class FlutterPlugin implements Plugin<Project> {
getPluginList().each this.&configurePluginProject getPluginList().each this.&configurePluginProject
return return
} }
if (useLocalEngine()) {
throw new GradleException("Local engine isn't supported when building the plugins as AAR")
}
project.repositories { project.repositories {
maven { maven {
url "${getPluginBuildDir()}/outputs/repo" url "${getPluginBuildDir()}/outputs/repo"
...@@ -326,24 +317,13 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -326,24 +317,13 @@ class FlutterPlugin implements Plugin<Project> {
// In AGP 3.5, the embedding must be added as an API implementation, // In AGP 3.5, the embedding must be added as an API implementation,
// so java8 features are desugared against the runtime classpath. // so java8 features are desugared against the runtime classpath.
// For more, see https://github.com/flutter/flutter/issues/40126 // For more, see https://github.com/flutter/flutter/issues/40126
if (flutterJar) { if (!supportsBuildMode(flutterBuildMode)) {
addApiDependencies(
pluginProject,
buildType.name,
project.files {
flutterJar
}
)
return return
} }
addApiDependencies( addApiDependencies(
pluginProject, pluginProject,
buildType.name, buildType.name,
"io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion", "io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion"
{
// Include the embedding transitive dependencies since plugins may depend on them.
transitive = true
}
) )
} }
pluginProject.afterEvaluate { pluginProject.afterEvaluate {
...@@ -425,7 +405,7 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -425,7 +405,7 @@ class FlutterPlugin implements Plugin<Project> {
} }
private Boolean useLocalEngine() { private Boolean useLocalEngine() {
return project.hasProperty('localEngineOut') return project.hasProperty('local-engine-repo')
} }
private Boolean isVerbose() { private Boolean isVerbose() {
...@@ -452,6 +432,19 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -452,6 +432,19 @@ class FlutterPlugin implements Plugin<Project> {
return System.getProperty('build-plugins-as-aars') == 'true' return System.getProperty('build-plugins-as-aars') == 'true'
} }
// Returns true if the build mode is supported by the current call to Gradle.
// This only relevant when using a local engine. Because the engine
// is built for a specific mode, the call to Gradle must match that mode.
private Boolean supportsBuildMode(String flutterBuildMode) {
if (!useLocalEngine()) {
return true;
}
assert project.hasProperty('local-engine-build-mode')
// Don't configure dependencies for a build mode that the local engine
// doesn't support.
return project.property('local-engine-build-mode') == flutterBuildMode
}
private void addCompileOnlyDependency(Project project, String variantName, Object dependency, Closure config = null) { private void addCompileOnlyDependency(Project project, String variantName, Object dependency, Closure config = null) {
if (project.state.failure) { if (project.state.failure) {
return return
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:xml/xml.dart' as xml;
import '../android/android_sdk.dart'; import '../android/android_sdk.dart';
import '../artifacts.dart'; import '../artifacts.dart';
...@@ -282,8 +283,17 @@ Future<void> buildGradleApp({ ...@@ -282,8 +283,17 @@ Future<void> buildGradleApp({
} }
if (artifacts is LocalEngineArtifacts) { if (artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = artifacts; final LocalEngineArtifacts localEngineArtifacts = artifacts;
printTrace('Using local engine: ${localEngineArtifacts.engineOutPath}'); final Directory localEngineRepo = _getLocalEngineRepo(
command.add('-PlocalEngineOut=${localEngineArtifacts.engineOutPath}'); engineOutPath: localEngineArtifacts.engineOutPath,
androidBuildInfo: androidBuildInfo,
);
printTrace(
'Using local engine: ${localEngineArtifacts.engineOutPath}\n'
'Local Maven repo: ${localEngineRepo.path}'
);
command.add('-Plocal-engine-repo=${localEngineRepo.path}');
command.add('-Plocal-engine-build-mode=${buildInfo.modeName}');
command.add('-Plocal-engine-out=${localEngineArtifacts.engineOutPath}');
} }
if (target != null) { if (target != null) {
command.add('-Ptarget=$target'); command.add('-Ptarget=$target');
...@@ -504,8 +514,17 @@ Future<void> buildGradleAar({ ...@@ -504,8 +514,17 @@ Future<void> buildGradleAar({
} }
if (artifacts is LocalEngineArtifacts) { if (artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = artifacts; final LocalEngineArtifacts localEngineArtifacts = artifacts;
printTrace('Using local engine: ${localEngineArtifacts.engineOutPath}'); final Directory localEngineRepo = _getLocalEngineRepo(
command.add('-PlocalEngineOut=${localEngineArtifacts.engineOutPath}'); engineOutPath: localEngineArtifacts.engineOutPath,
androidBuildInfo: androidBuildInfo,
);
printTrace(
'Using local engine: ${localEngineArtifacts.engineOutPath}\n'
'Local Maven repo: ${localEngineRepo.path}'
);
command.add('-Plocal-engine-repo=${localEngineRepo.path}');
command.add('-Plocal-engine-build-mode=${androidBuildInfo.buildInfo.modeName}');
command.add('-Plocal-engine-out=${localEngineArtifacts.engineOutPath}');
} }
command.add(aarTask); command.add(aarTask);
...@@ -781,3 +800,110 @@ void _exitWithExpectedFileNotFound({ ...@@ -781,3 +800,110 @@ void _exitWithExpectedFileNotFound({
'but the tool couldn\'t find it.' 'but the tool couldn\'t find it.'
); );
} }
void _createSymlink(String targetPath, String linkPath) {
final File targetFile = fs.file(targetPath);
if (!targetFile.existsSync()) {
throwToolExit('The file $targetPath wasn\'t found in the local engine out directory.');
}
final File linkFile = fs.file(linkPath);
final Link symlink = linkFile.parent.childLink(linkFile.basename);
try {
symlink.createSync(targetPath, recursive: true);
} on FileSystemException catch (exception) {
throwToolExit(
'Failed to create the symlink $linkPath->$targetPath: $exception'
);
}
}
String _getLocalArtifactVersion(String pomPath) {
final File pomFile = fs.file(pomPath);
if (!pomFile.existsSync()) {
throwToolExit('The file $pomPath wasn\'t found in the local engine out directory.');
}
xml.XmlDocument document;
try {
document = xml.parse(pomFile.readAsStringSync());
} on xml.XmlParserException {
throwToolExit(
'Error parsing $pomPath. Please ensure that this is a valid XML document.'
);
} on FileSystemException {
throwToolExit(
'Error reading $pomPath. Please ensure that you have read permission to this'
'file and try again.');
}
final Iterable<xml.XmlElement> project = document.findElements('project');
assert(project.isNotEmpty);
for (xml.XmlElement versionElement in document.findAllElements('version')) {
if (versionElement.parent == project.first) {
return versionElement.text;
}
}
throwToolExit('Error while parsing the <version> element from $pomPath');
return null;
}
/// Returns the local Maven repository for a local engine build.
/// For example, if the engine is built locally at <home>/engine/src/out/android_release_unopt
/// This method generates symlinks in the temp directory to the engine artifacts
/// following the convention specified on https://maven.apache.org/pom.html#Repositories
Directory _getLocalEngineRepo({
@required String engineOutPath,
@required AndroidBuildInfo androidBuildInfo,
}) {
assert(engineOutPath != null);
assert(androidBuildInfo != null);
final String abi = getEnumName(androidBuildInfo.targetArchs.first);
final Directory localEngineRepo = fs.systemTempDirectory
.createTempSync('flutter_tool_local_engine_repo.');
// Remove the local engine repo before the tool exits.
addShutdownHook(
() => localEngineRepo.deleteSync(recursive: true),
ShutdownStage.CLEANUP,
);
final String buildMode = androidBuildInfo.buildInfo.modeName;
final String artifactVersion = _getLocalArtifactVersion(
fs.path.join(
engineOutPath,
'flutter_embedding_$buildMode.pom',
)
);
for (String artifact in const <String>['pom', 'jar']) {
// The Android embedding artifacts.
_createSymlink(
fs.path.join(
engineOutPath,
'flutter_embedding_$buildMode.$artifact',
),
fs.path.join(
localEngineRepo.path,
'io',
'flutter',
'flutter_embedding_$buildMode',
artifactVersion,
'flutter_embedding_$buildMode-$artifactVersion.$artifact',
),
);
// The engine artifacts (libflutter.so).
_createSymlink(
fs.path.join(
engineOutPath,
'${abi}_$buildMode.$artifact',
),
fs.path.join(
localEngineRepo.path,
'io',
'flutter',
'${abi}_$buildMode',
artifactVersion,
'${abi}_$buildMode-$artifactVersion.$artifact',
),
);
}
return localEngineRepo;
}
...@@ -1484,7 +1484,7 @@ plugin2=${plugin2.path} ...@@ -1484,7 +1484,7 @@ plugin2=${plugin2.path}
), ),
target: 'lib/main.dart', target: 'lib/main.dart',
isBuildingBundle: false, isBuildingBundle: false,
localGradleErrors: <GradleHandledError>[], localGradleErrors: const <GradleHandledError>[],
); );
final BufferLogger logger = context.get<Logger>(); final BufferLogger logger = context.get<Logger>();
...@@ -1564,6 +1564,96 @@ Consuming the Module ...@@ -1564,6 +1564,96 @@ Consuming the Module
To learn more, visit https://flutter.dev/go/build-aar''')); To learn more, visit https://flutter.dev/go/build-aar'''));
}, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio,
Cache: () => cache,
Platform: () => android,
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('build apk uses selected local engine', () async {
when(mockArtifacts.getArtifactPath(Artifact.flutterFramework,
platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine');
when(mockArtifacts.engineOutPath).thenReturn(fs.path.join('out', 'android_arm'));
fs.file('out/android_arm/flutter_embedding_release.pom')
..createSync(recursive: true)
..writeAsStringSync(
'''<?xml version="1.0" encoding="UTF-8"?>
<project>
<version>1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b</version>
<dependencies>
</dependencies>
</project>
''');
fs.file('out/android_arm/armeabi_v7a_release.pom').createSync(recursive: true);
fs.file('out/android_arm/armeabi_v7a_release.jar').createSync(recursive: true);
fs.file('out/android_arm/flutter_embedding_release.jar').createSync(recursive: true);
fs.file('out/android_arm/flutter_embedding_release.pom').createSync(recursive: true);
fs.file('android/gradlew').createSync(recursive: true);
fs.directory('android')
.childFile('gradle.properties')
.createSync(recursive: true);
fs.file('android/build.gradle')
.createSync(recursive: true);
fs.directory('android')
.childDirectory('app')
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
// Let any process start. Assert after.
when(mockProcessManager.run(
any,
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenAnswer((_) async => ProcessResult(1, 0, '', ''));
when(mockProcessManager.start(any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
.thenAnswer((_) {
return Future<Process>.value(
createMockProcess(
exitCode: 1,
)
);
});
await expectLater(() async {
await buildGradleApp(
project: FlutterProject.current(),
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
null,
),
),
target: 'lib/main.dart',
isBuildingBundle: false,
localGradleErrors: const <GradleHandledError>[],
);
}, throwsToolExit());
final List<String> actualGradlewCall = verify(
mockProcessManager.start(
captureAny,
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory')
),
).captured.last;
expect(actualGradlewCall, contains('/android/gradlew'));
expect(actualGradlewCall, contains('-Plocal-engine-out=out/android_arm'));
expect(actualGradlewCall, contains('-Plocal-engine-repo=/.tmp_rand0/flutter_tool_local_engine_repo.rand0'));
expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk, AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio, AndroidStudio: () => mockAndroidStudio,
...@@ -1579,6 +1669,21 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ...@@ -1579,6 +1669,21 @@ To learn more, visit https://flutter.dev/go/build-aar'''));
platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine'); platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine');
when(mockArtifacts.engineOutPath).thenReturn(fs.path.join('out', 'android_arm')); when(mockArtifacts.engineOutPath).thenReturn(fs.path.join('out', 'android_arm'));
fs.file('out/android_arm/flutter_embedding_release.pom')
..createSync(recursive: true)
..writeAsStringSync(
'''<?xml version="1.0" encoding="UTF-8"?>
<project>
<version>1.0.0-73fd6b049a80bcea2db1f26c7cee434907cd188b</version>
<dependencies>
</dependencies>
</project>
''');
fs.file('out/android_arm/armeabi_v7a_release.pom').createSync(recursive: true);
fs.file('out/android_arm/armeabi_v7a_release.jar').createSync(recursive: true);
fs.file('out/android_arm/flutter_embedding_release.jar').createSync(recursive: true);
fs.file('out/android_arm/flutter_embedding_release.pom').createSync(recursive: true);
final File manifestFile = fs.file('pubspec.yaml'); final File manifestFile = fs.file('pubspec.yaml');
manifestFile.createSync(recursive: true); manifestFile.createSync(recursive: true);
manifestFile.writeAsStringSync(''' manifestFile.writeAsStringSync('''
...@@ -1588,8 +1693,7 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ...@@ -1588,8 +1693,7 @@ To learn more, visit https://flutter.dev/go/build-aar'''));
''' '''
); );
final File gradlew = fs.file('.android/gradlew'); fs.file('.android/gradlew').createSync(recursive: true);
gradlew.createSync(recursive: true);
fs.file('.android/gradle.properties') fs.file('.android/gradle.properties')
.writeAsStringSync('irrelevant'); .writeAsStringSync('irrelevant');
...@@ -1597,12 +1701,6 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ...@@ -1597,12 +1701,6 @@ To learn more, visit https://flutter.dev/go/build-aar'''));
fs.file('.android/build.gradle') fs.file('.android/build.gradle')
.createSync(recursive: true); .createSync(recursive: true);
when(mockProcessManager.run(
<String> ['.android/gradlew', '-v'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(1, 0, '5.1.1', ''));
// Let any process start. Assert after. // Let any process start. Assert after.
when(mockProcessManager.run( when(mockProcessManager.run(
any, any,
...@@ -1619,14 +1717,19 @@ To learn more, visit https://flutter.dev/go/build-aar''')); ...@@ -1619,14 +1717,19 @@ To learn more, visit https://flutter.dev/go/build-aar'''));
target: '', target: '',
); );
final List<String> actualGradlewCall = verify(mockProcessManager.run( final List<String> actualGradlewCall = verify(
mockProcessManager.run(
captureAny, captureAny,
environment: anyNamed('environment'), environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory')), workingDirectory: anyNamed('workingDirectory'),
),
).captured.last; ).captured.last;
expect(actualGradlewCall, contains('/.android/gradlew')); expect(actualGradlewCall, contains('/.android/gradlew'));
expect(actualGradlewCall, contains('-PlocalEngineOut=out/android_arm')); expect(actualGradlewCall, contains('-Plocal-engine-out=out/android_arm'));
expect(actualGradlewCall, contains('-Plocal-engine-repo=/.tmp_rand0/flutter_tool_local_engine_repo.rand0'));
expect(actualGradlewCall, contains('-Plocal-engine-build-mode=release'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk, AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio, AndroidStudio: () => mockAndroidStudio,
...@@ -1661,7 +1764,9 @@ FlutterProject generateFakeAppBundle(String directoryName, String fileName) { ...@@ -1661,7 +1764,9 @@ FlutterProject generateFakeAppBundle(String directoryName, String fileName) {
} }
Platform fakePlatform(String name) { Platform fakePlatform(String name) {
return FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = name; return FakePlatform.fromPlatform(const LocalPlatform())
..operatingSystem = name
..stdoutSupportsAnsi = false;
} }
class FakeGradleUtils extends GradleUtils { class FakeGradleUtils extends GradleUtils {
......
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