Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
96482eeb
Unverified
Commit
96482eeb
authored
Sep 20, 2019
by
Emmanuel Garcia
Committed by
GitHub
Sep 20, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Re-enable AAR plugins when an AndroidX failure occurred (#40810)
parent
c238045a
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
401 additions
and
247 deletions
+401
-247
test.dart
dev/bots/test.dart
+1
-0
gradle_jetifier_test.dart
dev/devicelab/bin/tasks/gradle_jetifier_test.dart
+4
-1
aar_init_script.gradle
packages/flutter_tools/gradle/aar_init_script.gradle
+27
-10
flutter.gradle
packages/flutter_tools/gradle/flutter.gradle
+54
-156
gradle.dart
packages/flutter_tools/lib/src/android/gradle.dart
+149
-53
features.dart
packages/flutter_tools/lib/src/features.dart
+0
-3
events.dart
packages/flutter_tools/lib/src/reporting/events.dart
+4
-0
usage.dart
packages/flutter_tools/lib/src/reporting/usage.dart
+1
-0
gradle_test.dart
...flutter_tools/test/general.shard/android/gradle_test.dart
+161
-5
features_test.dart
packages/flutter_tools/test/general.shard/features_test.dart
+0
-15
testbed.dart
packages/flutter_tools/test/src/testbed.dart
+0
-4
No files found.
dev/bots/test.dart
View file @
96482eeb
...
...
@@ -1022,6 +1022,7 @@ Future<void> _androidGradleTests(String subShard) async {
await
_runDevicelabTest
(
'gradle_plugin_fat_apk_test'
,
env:
env
);
await
_runDevicelabTest
(
'gradle_r8_test'
,
env:
env
);
await
_runDevicelabTest
(
'gradle_non_android_plugin_test'
,
env:
env
);
await
_runDevicelabTest
(
'gradle_jetifier_test'
,
env:
env
);
}
if
(
subShard
==
'gradle2'
)
{
await
_runDevicelabTest
(
'gradle_plugin_bundle_test'
,
env:
env
);
...
...
dev/devicelab/bin/tasks/gradle_jetifier_test.dart
View file @
96482eeb
...
...
@@ -64,6 +64,7 @@ Future<void> main() async {
options:
<
String
>[
'apk'
,
'--target-platform'
,
'android-arm'
,
'--no-shrink'
,
'--verbose'
,
],
);
...
...
@@ -100,7 +101,9 @@ Future<void> main() async {
options:
<
String
>[
'apk'
,
'--target-platform'
,
'android-arm'
,
'--debug'
,
'--verbose'
,
'--debug'
,
'--no-shrink'
,
'--verbose'
,
],
);
});
...
...
packages/flutter_tools/gradle/aar_init_script.gradle
View file @
96482eeb
...
...
@@ -7,6 +7,7 @@
// destination of the local repository.
// The local repository will contain the AAR and POM files.
import
java.nio.file.Paths
import
org.gradle.api.Project
import
org.gradle.api.artifacts.Configuration
import
org.gradle.api.artifacts.maven.MavenDeployer
...
...
@@ -39,16 +40,32 @@ void configureProject(Project project, File outputDir) {
}
}
}
// Check if the project uses the Flutter plugin (defined in flutter.gradle).
Boolean
usesFlutterPlugin
=
project
.
plugins
.
find
{
it
.
class
.
name
==
"FlutterPlugin"
}
!=
null
if
(!
usesFlutterPlugin
)
{
if
(!
project
.
property
(
"is-plugin"
).
toBoolean
())
{
return
}
if
(
project
.
hasProperty
(
'localEngineOut'
))
{
// TODO(egarciad): Support local engine.
// This most likely requires refactoring `flutter.gradle`, so the logic can be reused.
throw
new
GradleException
(
"Local engine isn't supported when building the plugins as AAR. "
+
"See: https://github.com/flutter/flutter/issues/40866"
)
}
// This is a Flutter plugin project. Plugin projects don't apply the Flutter Gradle plugin,
// as a result, add the dependency on the embedding.
project
.
repositories
{
maven
{
url
"http://download.flutter.io"
}
}
String
engineVersion
=
Paths
.
get
(
getFlutterRoot
(
project
),
"bin"
,
"internal"
,
"engine.version"
)
.
toFile
().
text
.
trim
()
project
.
dependencies
{
// Some plugins don't include `annotations` and they don't set
// `android.useAndroidX=true` in `gradle.properties`.
compileOnly
"androidx.annotation:annotation:+"
compileOnly
"com.android.support:support-annotations:+"
// The Flutter plugin already adds `flutter.jar`.
compileOnly
project
.
files
(
"${getFlutterRoot(project)}/bin/cache/artifacts/engine/android-arm-release/flutter.jar"
)
// Add the embedding dependency.
compileOnly
(
"io.flutter:flutter_embedding_release:1.0.0-$engineVersion"
)
{
// We only need to expose io.flutter.plugin.*
// No need for the embedding transitive dependencies.
transitive
=
false
}
}
}
...
...
packages/flutter_tools/gradle/flutter.gradle
View file @
96482eeb
...
...
@@ -8,6 +8,8 @@ import com.android.builder.model.AndroidProject
import
com.android.build.OutputFile
import
java.nio.file.Path
import
java.nio.file.Paths
import
java.util.regex.Matcher
import
java.util.regex.Pattern
import
org.apache.tools.ant.taskdefs.condition.Os
import
org.gradle.api.DefaultTask
import
org.gradle.api.GradleException
...
...
@@ -256,29 +258,65 @@ class FlutterPlugin implements Plugin<Project> {
*/
private
void
configurePlugins
()
{
if
(!
buildPluginAsAar
())
{
getPluginList
().
each
this
.&
configurePlugin
getPluginList
().
each
this
.&
configurePlugin
Project
return
}
addPluginTasks
()
List
<
String
>
tasksToExecute
=
project
.
gradle
.
startParameter
.
taskNames
Set
buildTypes
=
getBuildTypesForTasks
(
tasksToExecute
)
if
(
tasksToExecute
.
contains
(
"clean"
))
{
// Because the plugins are built during configuration, the task "clean"
// cannot run in conjunction with an assembly task.
if
(!
buildTypes
.
empty
)
{
throw
new
GradleException
(
"Can't run the clean task along with other assemble tasks"
)
if
(
useLocalEngine
())
{
throw
new
GradleException
(
"Local engine isn't supported when building the plugins as AAR"
)
}
List
<
Project
>
projects
=
[
project
]
// Module projects set the `hostProjects` extra property in `include_flutter.groovy`.
// This is required to set the local repository in each host app project.
if
(
project
.
ext
.
has
(
"hostProjects"
))
{
projects
.
addAll
(
project
.
ext
.
get
(
"hostProjects"
))
}
// Configure the repository for the plugins.
projects
.
each
{
hostProject
->
hostProject
.
repositories
{
maven
{
url
"${getPluginBuildDir()}/outputs/repo"
}
}
}
// Build plugins when a task "assembly*" will be called later.
if
(!
buildTypes
.
empty
)
{
// Build the plugin during configuration.
// This is required when Jetifier is enabled, otherwise the implementation dependency
// cannot be added.
buildAarPlugins
(
buildTypes
)
getPluginList
().
each
{
pluginName
,
pluginPath
->
configurePluginAar
(
pluginName
,
pluginPath
,
project
)
}
}
private
void
configurePlugin
(
String
name
,
String
_
)
{
private
static
final
Pattern
GROUP_PATTERN
=
~
/group\s+\'(.+)\'/
private
static
final
Pattern
PROJECT_NAME_PATTERN
=
~
/rootProject\.name\s+=\s+\'(.+)\'/
// Adds the plugin AAR dependency to the app project.
private
void
configurePluginAar
(
String
pluginName
,
String
pluginPath
,
Project
project
)
{
// Extract the group id from the plugin's build.gradle.
// This is `group '<group-id>'`
File
pluginBuildFile
=
project
.
file
(
Paths
.
get
(
pluginPath
,
"android"
,
"build.gradle"
));
if
(!
pluginBuildFile
.
exists
())
{
throw
new
GradleException
(
"Plugin $pluginName doesn't have the required file $pluginBuildFile."
)
}
Matcher
groupParts
=
GROUP_PATTERN
.
matcher
(
pluginBuildFile
.
text
)
assert
groupParts
.
count
==
1
assert
groupParts
.
hasGroup
()
String
groupId
=
groupParts
[
0
][
1
]
// Extract the artifact name from the plugin's settings.gradle.
// This is `rootProject.name = '<artifact-name>'`
File
pluginSettings
=
project
.
file
(
Paths
.
get
(
pluginPath
,
"android"
,
"settings.gradle"
));
if
(!
pluginSettings
.
exists
())
{
throw
new
GradleException
(
"Plugin $pluginName doesn't have the required file $pluginSettings."
)
}
Matcher
projectNameParts
=
PROJECT_NAME_PATTERN
.
matcher
(
pluginSettings
.
text
)
assert
projectNameParts
.
count
==
1
assert
projectNameParts
.
hasGroup
()
String
artifactId
=
"${projectNameParts[0][1]}_release"
assert
!
groupId
.
empty
project
.
dependencies
.
add
(
"api"
,
"$groupId:$artifactId:+"
)
}
// Adds the plugin project dependency to the app project .
private
void
configurePluginProject
(
String
name
,
String
_
)
{
Project
pluginProject
=
project
.
rootProject
.
findProject
(
":$name"
)
if
(
pluginProject
==
null
)
{
project
.
logger
.
error
(
"Plugin project :$name not found. Please update settings.gradle."
)
...
...
@@ -343,93 +381,6 @@ class FlutterPlugin implements Plugin<Project> {
return
androidPlugins
}
private
void
addPluginTasks
()
{
Properties
plugins
=
getPluginList
()
project
.
android
.
buildTypes
.
each
{
buildType
->
plugins
.
each
{
name
,
path
->
String
buildModeValue
=
buildType
.
debuggable
?
"debug"
:
"release"
List
<
String
>
taskNameParts
=
[
"build"
,
"plugin"
,
buildModeValue
]
taskNameParts
.
addAll
(
name
.
split
(
"_"
))
String
taskName
=
toCammelCase
(
taskNameParts
)
// Build types can be extended. For example, a build type can extend the `debug` mode.
// In such cases, prevent creating the same task.
if
(
project
.
tasks
.
findByName
(
taskName
)
==
null
)
{
project
.
tasks
.
create
(
name:
taskName
,
type:
FlutterPluginTask
)
{
flutterExecutable
this
.
flutterExecutable
buildMode
buildModeValue
verbose
isVerbose
()
pluginDir
project
.
file
(
path
)
sourceDir
project
.
file
(
project
.
flutter
.
source
)
intermediateDir
getPluginBuildDir
()
}
}
}
}
}
private
void
buildAarPlugins
(
Set
buildTypes
)
{
List
<
Project
>
projects
=
[
project
]
// Module projects set the `hostProjects` extra property in `include_flutter.groovy`.
// This is required to set the local repository in each host app project.
if
(
project
.
ext
.
has
(
"hostProjects"
))
{
projects
.
addAll
(
project
.
ext
.
get
(
"hostProjects"
))
}
projects
.
each
{
hostProject
->
hostProject
.
repositories
{
maven
{
url
"${getPluginBuildDir()}/outputs/repo"
}
}
}
buildTypes
.
each
{
buildType
->
project
.
tasks
.
withType
(
FlutterPluginTask
).
all
{
pluginTask
->
String
buildMode
=
buildType
.
debuggable
?
"debug"
:
"release"
if
(
pluginTask
.
buildMode
!=
buildMode
)
{
return
}
pluginTask
.
execute
()
pluginTask
.
intermediateDir
.
eachFileRecurse
(
FILES
)
{
file
->
if
(
file
.
name
!=
"maven-metadata.xml"
)
{
return
}
def
mavenMetadata
=
new
XmlParser
().
parse
(
file
)
String
groupId
=
mavenMetadata
.
groupId
.
text
()
String
artifactId
=
mavenMetadata
.
artifactId
.
text
()
if
(!
artifactId
.
endsWith
(
buildMode
))
{
return
}
// Add the plugin dependency based on the Maven metadata.
addApiDependencies
(
project
,
buildType
.
name
,
"$groupId:$artifactId:+@aar"
,
{
transitive
=
true
})
}
}
}
}
/**
* Returns a set with the build type names that apply to the given list of tasks
* required to configure the plugin dependencies.
*/
private
Set
getBuildTypesForTasks
(
List
<
String
>
tasksToExecute
)
{
Set
buildTypes
=
[]
tasksToExecute
.
each
{
task
->
project
.
android
.
buildTypes
.
each
{
buildType
->
if
(
task
==
"androidDependencies"
||
task
.
endsWith
(
"dependencies"
))
{
// The tasks to query the dependencies includes all the build types.
buildTypes
.
add
(
buildType
)
}
else
if
(
task
.
endsWith
(
"assemble"
))
{
// The `assemble` task includes all the build types.
buildTypes
.
add
(
buildType
)
}
else
if
(
task
.
endsWith
(
buildType
.
name
.
capitalize
()))
{
buildTypes
.
add
(
buildType
)
}
}
}
return
buildTypes
}
private
static
String
toCammelCase
(
List
<
String
>
parts
)
{
if
(
parts
.
empty
)
{
return
""
...
...
@@ -920,56 +871,3 @@ class FlutterTask extends BaseFlutterTask {
buildBundle
()
}
}
class
FlutterPluginTask
extends
DefaultTask
{
File
flutterExecutable
@Optional
@Input
Boolean
verbose
@Input
String
buildMode
@Input
File
pluginDir
@Input
File
intermediateDir
File
sourceDir
@InputFiles
FileCollection
getSourceFiles
()
{
return
project
.
fileTree
(
dir:
sourceDir
,
exclude:
[
"android"
,
"ios"
],
include:
[
"pubspec.yaml"
]
)
}
@OutputDirectory
File
getOutputDirectory
()
{
return
intermediateDir
}
@TaskAction
void
build
()
{
intermediateDir
.
mkdirs
()
project
.
exec
{
executable
flutterExecutable
.
absolutePath
workingDir
pluginDir
args
"build"
,
"aar"
args
"--quiet"
args
"--suppress-analytics"
args
"--output-dir"
,
"${intermediateDir}"
switch
(
buildMode
)
{
case
'release'
:
args
"--release"
break
case
'debug'
:
args
"--debug"
break
default:
assert
false
}
if
(
verbose
)
{
args
"--verbose"
}
}
}
}
packages/flutter_tools/lib/src/android/gradle.dart
View file @
96482eeb
...
...
@@ -22,7 +22,6 @@ import '../base/utils.dart';
import
'../base/version.dart'
;
import
'../build_info.dart'
;
import
'../cache.dart'
;
import
'../features.dart'
;
import
'../flutter_manifest.dart'
;
import
'../globals.dart'
;
import
'../project.dart'
;
...
...
@@ -47,18 +46,20 @@ class GradleUtils {
return
_cachedExecutable
;
}
GradleProject
_cachedAppProject
;
/// Gets the [GradleProject] for the current [FlutterProject] if built as an app.
Future
<
GradleProject
>
get
appProject
async
{
_cachedAppProject
??=
await
_readGradleProject
(
isLibrary:
false
);
return
_cachedAppProject
;
final
Map
<
FlutterProject
,
GradleProject
>
_cachedAppProject
=
<
FlutterProject
,
GradleProject
>{};
/// Gets the [GradleProject] for the [project] if built as an app.
Future
<
GradleProject
>
getAppProject
(
FlutterProject
project
)
async
{
_cachedAppProject
[
project
]
??=
await
_readGradleProject
(
project
,
isLibrary:
false
);
return
_cachedAppProject
[
project
];
}
GradleProject
_cachedLibraryProject
;
/// Gets the [GradleProject] for the current [FlutterProject] if built as a library.
Future
<
GradleProject
>
get
libraryProject
async
{
_cachedLibraryProject
??=
await
_readGradleProject
(
isLibrary:
true
);
return
_cachedLibraryProject
;
final
Map
<
FlutterProject
,
GradleProject
>
_cachedLibraryProject
=
<
FlutterProject
,
GradleProject
>{};
/// Gets the [GradleProject] for the [project] if built as a library.
Future
<
GradleProject
>
getLibraryProject
(
FlutterProject
project
)
async
{
_cachedLibraryProject
[
project
]
??=
await
_readGradleProject
(
project
,
isLibrary:
true
);
return
_cachedLibraryProject
[
project
];
}
}
...
...
@@ -133,7 +134,8 @@ Future<File> getGradleAppOut(AndroidProject androidProject) async {
case
FlutterPluginVersion
.
managed
:
// Fall through. The managed plugin matches plugin v2 for now.
case
FlutterPluginVersion
.
v2
:
final
GradleProject
gradleProject
=
await
gradleUtils
.
appProject
;
final
GradleProject
gradleProject
=
await
gradleUtils
.
getAppProject
(
FlutterProject
.
current
());
return
fs
.
file
(
gradleProject
.
apkDirectory
.
childFile
(
'app.apk'
));
}
return
null
;
...
...
@@ -209,8 +211,10 @@ void createSettingsAarGradle(Directory androidDirectory) {
// Note: Dependencies are resolved and possibly downloaded as a side-effect
// of calculating the app properties using Gradle. This may take minutes.
Future
<
GradleProject
>
_readGradleProject
({
bool
isLibrary
=
false
})
async
{
final
FlutterProject
flutterProject
=
FlutterProject
.
current
();
Future
<
GradleProject
>
_readGradleProject
(
FlutterProject
flutterProject
,
{
bool
isLibrary
=
false
,
})
async
{
final
String
gradlew
=
await
gradleUtils
.
getExecutable
(
flutterProject
);
updateLocalProperties
(
project:
flutterProject
);
...
...
@@ -218,10 +222,6 @@ Future<GradleProject> _readGradleProject({bool isLibrary = false}) async {
final
FlutterManifest
manifest
=
flutterProject
.
manifest
;
final
Directory
hostAppGradleRoot
=
flutterProject
.
android
.
hostAppGradleRoot
;
if
(
featureFlags
.
isPluginAsAarEnabled
&&
!
manifest
.
isPlugin
&&
!
manifest
.
isModule
)
{
createSettingsAarGradle
(
hostAppGradleRoot
);
}
if
(
manifest
.
isPlugin
)
{
assert
(
isLibrary
);
return
GradleProject
(
...
...
@@ -581,9 +581,9 @@ Future<void> buildGradleAar({
GradleProject
gradleProject
;
if
(
manifest
.
isModule
)
{
gradleProject
=
await
gradleUtils
.
appProject
;
gradleProject
=
await
gradleUtils
.
getAppProject
(
project
)
;
}
else
if
(
manifest
.
isPlugin
)
{
gradleProject
=
await
gradleUtils
.
libraryProject
;
gradleProject
=
await
gradleUtils
.
getLibraryProject
(
project
)
;
}
else
{
throwToolExit
(
'AARs can only be built for plugin or module projects.'
);
}
...
...
@@ -612,13 +612,11 @@ Future<void> buildGradleAar({
'-Pflutter-root=
$flutterRoot
'
,
'-Poutput-dir=
${gradleProject.buildDirectory}
'
,
'-Pis-plugin=
${manifest.isPlugin}
'
,
'-Dbuild-plugins-as-aars=true'
,
];
if
(
target
!=
null
&&
target
.
isNotEmpty
)
{
command
.
add
(
'-Ptarget=
$target
'
);
}
if
(
androidBuildInfo
.
targetArchs
.
isNotEmpty
)
{
final
String
targetPlatforms
=
androidBuildInfo
.
targetArchs
.
map
(
getPlatformNameForAndroidArch
).
join
(
','
);
...
...
@@ -633,34 +631,30 @@ Future<void> buildGradleAar({
command
.
add
(
aarTask
);
final
Stopwatch
sw
=
Stopwatch
()..
start
();
int
exitCode
=
1
;
RunResult
result
;
try
{
exitCode
=
await
processUtils
.
stream
(
result
=
await
processUtils
.
run
(
command
,
workingDirectory:
project
.
android
.
hostAppGradleRoot
.
path
,
allowReentrantFlutter:
true
,
environment:
gradleEnv
,
mapFunction:
(
String
line
)
{
// Always print the full line in verbose mode.
if
(
logger
.
isVerbose
)
{
return
line
;
}
return
null
;
},
);
}
finally
{
status
.
stop
();
}
flutterUsage
.
sendTiming
(
'build'
,
'gradle-aar'
,
Duration
(
milliseconds:
sw
.
elapsedMilliseconds
)
);
flutterUsage
.
sendTiming
(
'build'
,
'gradle-aar'
,
sw
.
elapsed
);
if
(
exitCode
!=
0
)
{
throwToolExit
(
'Gradle task
$aarTask
failed with exit code
$exitCode
'
,
exitCode:
exitCode
);
if
(
result
.
exitCode
!=
0
)
{
printStatus
(
result
.
stdout
,
wrap:
false
);
printError
(
result
.
stderr
,
wrap:
false
);
throwToolExit
(
'Gradle task
$aarTask
failed with exit code
$exitCode
.'
,
exitCode:
exitCode
);
}
final
Directory
repoDirectory
=
gradleProject
.
repoDirectory
;
if
(!
repoDirectory
.
existsSync
())
{
throwToolExit
(
'Gradle task
$aarTask
failed to produce
$repoDirectory
'
,
exitCode:
exitCode
);
printStatus
(
result
.
stdout
,
wrap:
false
);
printError
(
result
.
stderr
,
wrap:
false
);
throwToolExit
(
'Gradle task
$aarTask
failed to produce
$repoDirectory
.'
,
exitCode:
exitCode
);
}
printStatus
(
'Built
${fs.path.relative(repoDirectory.path)}
.'
,
color:
TerminalColor
.
green
);
}
...
...
@@ -730,21 +724,33 @@ Future<void> _buildGradleProjectV2(
FlutterProject
flutterProject
,
AndroidBuildInfo
androidBuildInfo
,
String
target
,
bool
isBuildingBundle
,
)
async
{
bool
isBuildingBundle
,
{
bool
shouldBuildPluginAsAar
=
false
,
})
async
{
final
String
gradlew
=
await
gradleUtils
.
getExecutable
(
flutterProject
);
final
GradleProject
project
=
await
gradleUtils
.
appProject
;
final
GradleProject
gradleProject
=
await
gradleUtils
.
getAppProject
(
flutterProject
);
if
(
shouldBuildPluginAsAar
)
{
// Create a settings.gradle that doesn't import the plugins as subprojects.
createSettingsAarGradle
(
flutterProject
.
android
.
hostAppGradleRoot
);
await
buildPluginsAsAar
(
flutterProject
,
androidBuildInfo
,
buildDirectory:
gradleProject
.
buildDirectory
,
);
}
final
BuildInfo
buildInfo
=
androidBuildInfo
.
buildInfo
;
String
assembleTask
;
if
(
isBuildingBundle
)
{
assembleTask
=
p
roject
.
bundleTaskFor
(
buildInfo
);
assembleTask
=
gradleP
roject
.
bundleTaskFor
(
buildInfo
);
}
else
{
assembleTask
=
p
roject
.
assembleTaskFor
(
buildInfo
);
assembleTask
=
gradleP
roject
.
assembleTaskFor
(
buildInfo
);
}
if
(
assembleTask
==
null
)
{
printUndefinedTask
(
p
roject
,
buildInfo
);
printUndefinedTask
(
gradleP
roject
,
buildInfo
);
throwToolExit
(
'Gradle build aborted.'
);
}
final
Status
status
=
logger
.
startProgress
(
...
...
@@ -791,14 +797,13 @@ Future<void> _buildGradleProjectV2(
.
map
(
getPlatformNameForAndroidArch
).
join
(
','
);
command
.
add
(
'-Ptarget-platform=
$targetPlatforms
'
);
}
if
(
featureFlags
.
isPluginAsAarEnabled
)
{
if
(
shouldBuildPluginAsAar
)
{
// Pass a system flag instead of a project flag, so this flag can be
// read from include_flutter.groovy.
command
.
add
(
'-Dbuild-plugins-as-aars=true'
);
if
(!
flutterProject
.
manifest
.
isModule
)
{
// Don't use settings.gradle from the current project since it includes the plugins as subprojects.
command
.
add
(
'--settings-file=settings_aar.gradle'
);
}
}
command
.
add
(
assembleTask
);
bool
potentialAndroidXFailure
=
false
;
bool
potentialR8Failure
=
false
;
...
...
@@ -844,24 +849,60 @@ Future<void> _buildGradleProjectV2(
printStatus
(
'To learn more, see: https://developer.android.com/studio/build/shrink-code'
,
indent:
4
);
BuildEvent
(
'r8-failure'
).
send
();
}
else
if
(
potentialAndroidXFailure
)
{
printStatus
(
'AndroidX incompatibilities may have caused this build to fail. See https://goo.gl/CP92wY.'
);
BuildEvent
(
'android-x-failure'
).
send
();
final
bool
hasPlugins
=
flutterProject
.
flutterPluginsFile
.
existsSync
();
final
bool
usesAndroidX
=
isAppUsingAndroidX
(
flutterProject
.
android
.
hostAppGradleRoot
);
if
(!
hasPlugins
)
{
// If the app doesn't use any plugin, then it's unclear where the incompatibility is coming from.
BuildEvent
(
'android-x-failure'
,
eventError:
'app-not-using-plugins'
).
send
();
}
if
(
hasPlugins
&&
!
usesAndroidX
)
{
// If the app isn't using AndroidX, then the app is likely using a plugin already migrated to AndroidX.
printStatus
(
'AndroidX incompatibilities may have caused this build to fail. '
);
printStatus
(
'Please migrate your app to AndroidX. See https://goo.gl/CP92wY.'
);
BuildEvent
(
'android-x-failure'
,
eventError:
'app-not-using-androidx'
).
send
();
}
if
(
hasPlugins
&&
usesAndroidX
&&
shouldBuildPluginAsAar
)
{
// This is a dependency conflict instead of an AndroidX failure since by this point
// the app is using AndroidX, the plugins are built as AARs, Jetifier translated
// Support libraries for AndroidX equivalents.
BuildEvent
(
'android-x-failure'
,
eventError:
'using-jetifier'
).
send
();
}
if
(
hasPlugins
&&
usesAndroidX
&&
!
shouldBuildPluginAsAar
)
{
printStatus
(
'The built failed likely due to AndroidX incompatibilities in a plugin. '
'The tool is about to try using Jetfier to solve the incompatibility.'
);
BuildEvent
(
'android-x-failure'
,
eventError:
'not-using-jetifier'
).
send
();
// The app is using Androidx, but Jetifier hasn't run yet.
// Call the current method again, build the plugins as AAR, so Jetifier can translate
// the dependencies.
// NOTE: Don't build the plugins as AARs by default since this drastically increases
// the build time.
await
_buildGradleProjectV2
(
flutterProject
,
androidBuildInfo
,
target
,
isBuildingBundle
,
shouldBuildPluginAsAar:
true
,
);
return
;
}
}
throwToolExit
(
'Gradle task
$assembleTask
failed with exit code
$exitCode
'
,
exitCode:
exitCode
);
}
flutterUsage
.
sendTiming
(
'build'
,
'gradle-v2'
,
Duration
(
milliseconds:
sw
.
elapsedMilliseconds
));
if
(!
isBuildingBundle
)
{
final
Iterable
<
File
>
apkFiles
=
findApkFiles
(
p
roject
,
androidBuildInfo
);
final
Iterable
<
File
>
apkFiles
=
findApkFiles
(
gradleP
roject
,
androidBuildInfo
);
if
(
apkFiles
.
isEmpty
)
{
throwToolExit
(
'Gradle build failed to produce an Android package.'
);
}
// Copy the first APK to app.apk, so `flutter run`, `flutter install`, etc. can find it.
// TODO(blasten): Handle multiple APKs.
apkFiles
.
first
.
copySync
(
p
roject
.
apkDirectory
.
childFile
(
'app.apk'
).
path
);
apkFiles
.
first
.
copySync
(
gradleP
roject
.
apkDirectory
.
childFile
(
'app.apk'
).
path
);
printTrace
(
'calculateSha:
${
p
roject.apkDirectory}
/app.apk'
);
final
File
apkShaFile
=
p
roject
.
apkDirectory
.
childFile
(
'app.apk.sha1'
);
printTrace
(
'calculateSha:
${
gradleP
roject.apkDirectory}
/app.apk'
);
final
File
apkShaFile
=
gradleP
roject
.
apkDirectory
.
childFile
(
'app.apk.sha1'
);
apkShaFile
.
writeAsStringSync
(
_calculateSha
(
apkFiles
.
first
));
for
(
File
apkFile
in
apkFiles
)
{
...
...
@@ -875,7 +916,7 @@ Future<void> _buildGradleProjectV2(
color:
TerminalColor
.
green
);
}
}
else
{
final
File
bundleFile
=
findBundleFile
(
p
roject
,
buildInfo
);
final
File
bundleFile
=
findBundleFile
(
gradleP
roject
,
buildInfo
);
if
(
bundleFile
==
null
)
{
throwToolExit
(
'Gradle build failed to produce an Android bundle package.'
);
}
...
...
@@ -891,6 +932,61 @@ Future<void> _buildGradleProjectV2(
}
}
/// Returns [true] if the current app uses AndroidX.
// TODO(egarciad): https://github.com/flutter/flutter/issues/40800
// Remove `FlutterManifest.usesAndroidX` and provide a unified `AndroidProject.usesAndroidX`.
@visibleForTesting
bool
isAppUsingAndroidX
(
Directory
androidDirectory
)
{
final
File
properties
=
androidDirectory
.
childFile
(
'gradle.properties'
);
if
(!
properties
.
existsSync
())
{
return
false
;
}
return
properties
.
readAsStringSync
().
contains
(
'android.useAndroidX=true'
);
}
/// Builds the plugins as AARs.
@visibleForTesting
Future
<
void
>
buildPluginsAsAar
(
FlutterProject
flutterProject
,
AndroidBuildInfo
androidBuildInfo
,
{
String
buildDirectory
,
})
async
{
final
File
flutterPluginFile
=
flutterProject
.
flutterPluginsFile
;
if
(!
flutterPluginFile
.
existsSync
())
{
return
;
}
final
List
<
String
>
plugins
=
flutterPluginFile
.
readAsStringSync
().
split
(
'
\n
'
);
for
(
String
plugin
in
plugins
)
{
final
List
<
String
>
pluginParts
=
plugin
.
split
(
'='
);
if
(
pluginParts
.
length
!=
2
)
{
continue
;
}
final
Directory
pluginDirectory
=
fs
.
directory
(
pluginParts
.
last
);
assert
(
pluginDirectory
.
existsSync
());
final
String
pluginName
=
pluginParts
.
first
;
logger
.
printStatus
(
'Building plugin
$pluginName
...'
);
try
{
await
buildGradleAar
(
project:
FlutterProject
.
fromDirectory
(
pluginDirectory
),
androidBuildInfo:
const
AndroidBuildInfo
(
BuildInfo
(
BuildMode
.
release
,
// Plugins are built as release.
null
,
// Plugins don't define flavors.
),
),
target:
''
,
outputDir:
buildDirectory
,
);
}
on
ToolExit
{
// Log the entire plugin entry in `.flutter-plugins` since it
// includes the plugin name and the version.
BuildEvent
(
'plugin-aar-failure'
,
eventError:
plugin
).
send
();
throwToolExit
(
'The plugin
$pluginName
could not be built due to the issue above. '
);
}
}
}
@visibleForTesting
Iterable
<
File
>
findApkFiles
(
GradleProject
project
,
AndroidBuildInfo
androidBuildInfo
)
{
final
Iterable
<
String
>
apkFileNames
=
project
.
apkFilesFor
(
androidBuildInfo
);
...
...
packages/flutter_tools/lib/src/features.dart
View file @
96482eeb
...
...
@@ -36,9 +36,6 @@ class FeatureFlags {
/// Whether flutter desktop for Windows is enabled.
bool
get
isWindowsEnabled
=>
_isEnabled
(
flutterWindowsDesktopFeature
);
/// Whether plugins are built as AARs in app projects.
bool
get
isPluginAsAarEnabled
=>
_isEnabled
(
flutterBuildPluginAsAarFeature
);
// Calculate whether a particular feature is enabled for the current channel.
static
bool
_isEnabled
(
Feature
feature
)
{
final
String
currentChannel
=
FlutterVersion
.
instance
.
channel
;
...
...
packages/flutter_tools/lib/src/reporting/events.dart
View file @
96482eeb
...
...
@@ -125,6 +125,7 @@ class BuildEvent extends UsageEvent {
BuildEvent
(
String
parameter
,
{
this
.
command
,
this
.
settings
,
this
.
eventError
,
})
:
super
(
'build'
+
(
FlutterCommand
.
current
==
null
?
''
:
'-
${FlutterCommand.current.name}
'
),
...
...
@@ -132,6 +133,7 @@ class BuildEvent extends UsageEvent {
final
String
command
;
final
String
settings
;
final
String
eventError
;
@override
void
send
()
{
...
...
@@ -140,6 +142,8 @@ class BuildEvent extends UsageEvent {
CustomDimensions
.
buildEventCommand
:
command
,
if
(
settings
!=
null
)
CustomDimensions
.
buildEventSettings
:
settings
,
if
(
eventError
!=
null
)
CustomDimensions
.
buildEventError
:
eventError
,
});
flutterUsage
.
sendEvent
(
category
,
parameter
,
parameters:
parameters
);
}
...
...
packages/flutter_tools/lib/src/reporting/usage.dart
View file @
96482eeb
...
...
@@ -53,6 +53,7 @@ enum CustomDimensions {
commandBuildApkSplitPerAbi
,
// cd40
commandBuildAppBundleTargetPlatform
,
// cd41
commandBuildAppBundleBuildMode
,
// cd42
buildEventError
,
// cd43
}
String
cdKey
(
CustomDimensions
cd
)
=>
'cd
${cd.index + 1}
'
;
...
...
packages/flutter_tools/test/general.shard/android/gradle_test.dart
View file @
96482eeb
...
...
@@ -25,7 +25,6 @@ import 'package:process/process.dart';
import
'../../src/common.dart'
;
import
'../../src/context.dart'
;
import
'../../src/mocks.dart'
;
import
'../../src/pubspec_schema.dart'
;
void
main
(
)
{
...
...
@@ -1155,6 +1154,153 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
});
});
group
(
'isAppUsingAndroidX'
,
()
{
FileSystem
fs
;
setUp
(()
{
fs
=
MemoryFileSystem
();
});
testUsingContext
(
'returns true when the project is using AndroidX'
,
()
async
{
final
Directory
androidDirectory
=
fs
.
systemTempDirectory
.
createTempSync
(
'android.'
);
androidDirectory
.
childFile
(
'gradle.properties'
)
.
writeAsStringSync
(
'android.useAndroidX=true'
);
expect
(
isAppUsingAndroidX
(
androidDirectory
),
isTrue
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'returns false when the project is not using AndroidX'
,
()
async
{
final
Directory
androidDirectory
=
fs
.
systemTempDirectory
.
createTempSync
(
'android.'
);
androidDirectory
.
childFile
(
'gradle.properties'
)
.
writeAsStringSync
(
'android.useAndroidX=false'
);
expect
(
isAppUsingAndroidX
(
androidDirectory
),
isFalse
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
testUsingContext
(
'returns false when gradle.properties does not exist'
,
()
async
{
final
Directory
androidDirectory
=
fs
.
systemTempDirectory
.
createTempSync
(
'android.'
);
expect
(
isAppUsingAndroidX
(
androidDirectory
),
isFalse
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
});
});
group
(
'buildPluginsAsAar'
,
()
{
FileSystem
fs
;
MockProcessManager
mockProcessManager
;
MockAndroidSdk
mockAndroidSdk
;
setUp
(()
{
fs
=
MemoryFileSystem
();
mockProcessManager
=
MockProcessManager
();
when
(
mockProcessManager
.
run
(
any
,
workingDirectory:
anyNamed
(
'workingDirectory'
),
environment:
anyNamed
(
'environment'
),
)).
thenAnswer
((
_
)
async
=>
ProcessResult
(
1
,
0
,
''
,
''
));
mockAndroidSdk
=
MockAndroidSdk
();
when
(
mockAndroidSdk
.
directory
).
thenReturn
(
'irrelevant'
);
});
testUsingContext
(
'calls gradle'
,
()
async
{
final
Directory
androidDirectory
=
fs
.
directory
(
'android.'
);
androidDirectory
.
createSync
();
androidDirectory
.
childFile
(
'pubspec.yaml'
)
.
writeAsStringSync
(
'name: irrelevant'
);
final
Directory
plugin1
=
fs
.
directory
(
'plugin1.'
);
plugin1
..
createSync
()
..
childFile
(
'pubspec.yaml'
)
.
writeAsStringSync
(
'''
name: irrelevant
flutter:
plugin:
androidPackage: irrelevant
'''
);
final
Directory
plugin2
=
fs
.
directory
(
'plugin2.'
);
plugin2
..
createSync
()
..
childFile
(
'pubspec.yaml'
)
.
writeAsStringSync
(
'''
name: irrelevant
flutter:
plugin:
androidPackage: irrelevant
'''
);
androidDirectory
.
childFile
(
'.flutter-plugins'
)
.
writeAsStringSync
(
'''
plugin1=
${plugin1.path}
plugin2=
${plugin2.path}
'''
);
final
Directory
buildDirectory
=
androidDirectory
.
childDirectory
(
'build'
);
buildDirectory
.
childDirectory
(
'outputs'
)
.
childDirectory
(
'repo'
)
.
createSync
(
recursive:
true
);
await
buildPluginsAsAar
(
FlutterProject
.
fromPath
(
androidDirectory
.
path
),
const
AndroidBuildInfo
(
BuildInfo
.
release
),
buildDirectory:
buildDirectory
.
path
,
);
final
String
flutterRoot
=
fs
.
path
.
absolute
(
Cache
.
flutterRoot
);
final
String
initScript
=
fs
.
path
.
join
(
flutterRoot
,
'packages'
,
'flutter_tools'
,
'gradle'
,
'aar_init_script.gradle'
);
verify
(
mockProcessManager
.
run
(
<
String
>[
'gradlew'
,
'-I=
$initScript
'
,
'-Pflutter-root=
$flutterRoot
'
,
'-Poutput-dir=
${buildDirectory.path}
'
,
'-Pis-plugin=true'
,
'-Ptarget-platform=android-arm,android-arm64'
,
'assembleAarRelease'
,
],
environment:
anyNamed
(
'environment'
),
workingDirectory:
plugin1
.
childDirectory
(
'android'
).
path
),
).
called
(
1
);
verify
(
mockProcessManager
.
run
(
<
String
>[
'gradlew'
,
'-I=
$initScript
'
,
'-Pflutter-root=
$flutterRoot
'
,
'-Poutput-dir=
${buildDirectory.path}
'
,
'-Pis-plugin=true'
,
'-Ptarget-platform=android-arm,android-arm64'
,
'assembleAarRelease'
,
],
environment:
anyNamed
(
'environment'
),
workingDirectory:
plugin2
.
childDirectory
(
'android'
).
path
),
).
called
(
1
);
},
overrides:
<
Type
,
Generator
>{
AndroidSdk:
()
=>
mockAndroidSdk
,
FileSystem:
()
=>
fs
,
GradleUtils:
()
=>
FakeGradleUtils
(),
ProcessManager:
()
=>
mockProcessManager
,
});
});
group
(
'gradle build'
,
()
{
MockAndroidSdk
mockAndroidSdk
;
MockAndroidStudio
mockAndroidStudio
;
...
...
@@ -1235,11 +1381,13 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
fs
.
currentDirectory
=
'path/to/project'
;
// Let any process start. Assert after.
when
(
mockProcessManager
.
start
(
when
(
mockProcessManager
.
run
(
any
,
environment:
anyNamed
(
'environment'
),
workingDirectory:
anyNamed
(
'workingDirectory'
))
).
thenAnswer
((
Invocation
invocation
)
=>
Future
<
Process
>.
value
(
MockProcess
()));
).
thenAnswer
(
(
_
)
async
=>
ProcessResult
(
1
,
0
,
''
,
''
),
);
fs
.
directory
(
'build/outputs/repo'
).
createSync
(
recursive:
true
);
await
buildGradleAar
(
...
...
@@ -1249,11 +1397,11 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
target:
''
);
final
List
<
String
>
actualGradlewCall
=
verify
(
mockProcessManager
.
start
(
final
List
<
String
>
actualGradlewCall
=
verify
(
mockProcessManager
.
run
(
captureAny
,
environment:
anyNamed
(
'environment'
),
workingDirectory:
anyNamed
(
'workingDirectory'
)),
).
captured
.
single
;
).
captured
.
last
;
expect
(
actualGradlewCall
,
contains
(
'/path/to/project/.android/gradlew'
));
expect
(
actualGradlewCall
,
contains
(
'-PlocalEngineOut=out/android_arm'
));
...
...
@@ -1284,6 +1432,14 @@ Platform fakePlatform(String name) {
return
FakePlatform
.
fromPlatform
(
const
LocalPlatform
())..
operatingSystem
=
name
;
}
class
FakeGradleUtils
extends
GradleUtils
{
@override
Future
<
String
>
getExecutable
(
FlutterProject
project
)
async
{
return
'gradlew'
;
}
}
class
MockAndroidSdk
extends
Mock
implements
AndroidSdk
{}
class
MockAndroidStudio
extends
Mock
implements
AndroidStudio
{}
class
MockDirectory
extends
Mock
implements
Directory
{}
class
MockFile
extends
Mock
implements
File
{}
...
...
packages/flutter_tools/test/general.shard/features_test.dart
View file @
96482eeb
...
...
@@ -432,21 +432,6 @@ void main() {
expect
(
featureFlags
.
isWindowsEnabled
,
false
);
}));
/// Plugins as AARS
test
(
'plugins built as AARs with config on master'
,
()
=>
testbed
.
run
(()
{
when
(
mockFlutterVerion
.
channel
).
thenReturn
(
'master'
);
when
<
bool
>(
mockFlutterConfig
.
getValue
(
'enable-build-plugin-as-aar'
)).
thenReturn
(
false
);
expect
(
featureFlags
.
isPluginAsAarEnabled
,
false
);
}));
test
(
'plugins built as AARs with config on dev'
,
()
=>
testbed
.
run
(()
{
when
(
mockFlutterVerion
.
channel
).
thenReturn
(
'dev'
);
when
<
bool
>(
mockFlutterConfig
.
getValue
(
'enable-build-plugin-as-aar'
)).
thenReturn
(
false
);
expect
(
featureFlags
.
isPluginAsAarEnabled
,
false
);
}));
});
}
...
...
packages/flutter_tools/test/src/testbed.dart
View file @
96482eeb
...
...
@@ -693,7 +693,6 @@ class TestFeatureFlags implements FeatureFlags {
this
.
isMacOSEnabled
=
false
,
this
.
isWebEnabled
=
false
,
this
.
isWindowsEnabled
=
false
,
this
.
isPluginAsAarEnabled
=
false
,
});
@override
...
...
@@ -707,7 +706,4 @@ class TestFeatureFlags implements FeatureFlags {
@override
final
bool
isWindowsEnabled
;
@override
final
bool
isPluginAsAarEnabled
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment