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
2df7dca8
Unverified
Commit
2df7dca8
authored
Jan 13, 2022
by
stuartmorgan
Committed by
GitHub
Jan 13, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fully support Dart-only mobile and macOS plugins (#96183)
parent
f18d4d03
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
305 additions
and
37 deletions
+305
-37
module_test_ios.dart
dev/devicelab/bin/tasks/module_test_ios.dart
+66
-4
plugin_test.dart
dev/devicelab/bin/tasks/plugin_test.dart
+2
-0
plugin_test_ios.dart
dev/devicelab/bin/tasks/plugin_test_ios.dart
+3
-0
plugin_tests.dart
dev/devicelab/lib/tasks/plugin_tests.dart
+78
-23
podhelper.rb
packages/flutter_tools/bin/podhelper.rb
+2
-1
app_plugin_loader.gradle
packages/flutter_tools/gradle/app_plugin_loader.gradle
+6
-0
module_plugin_loader.gradle
packages/flutter_tools/gradle/module_plugin_loader.gradle
+6
-0
flutter_plugins.dart
packages/flutter_tools/lib/src/flutter_plugins.dart
+7
-1
podhelper.rb.tmpl
...mplates/module/ios/library/Flutter.tmpl/podhelper.rb.tmpl
+2
-1
plugins_test.dart
packages/flutter_tools/test/general.shard/plugins_test.dart
+133
-7
No files found.
dev/devicelab/bin/tasks/module_test_ios.dart
View file @
2df7dca8
...
@@ -152,14 +152,28 @@ Future<void> main() async {
...
@@ -152,14 +152,28 @@ Future<void> main() async {
await
flutter
(
'clean'
);
await
flutter
(
'clean'
);
});
});
// Make a fake Dart-only plugin, since there are no existing examples.
section
(
'Create local plugin'
);
const
String
dartPluginName
=
'dartplugin'
;
await
_createFakeDartPlugin
(
dartPluginName
,
tempDir
);
section
(
'Add plugins'
);
section
(
'Add plugins'
);
final
File
pubspec
=
File
(
path
.
join
(
projectDir
.
path
,
'pubspec.yaml'
));
final
File
pubspec
=
File
(
path
.
join
(
projectDir
.
path
,
'pubspec.yaml'
));
String
content
=
await
pubspec
.
readAsString
();
String
content
=
await
pubspec
.
readAsString
();
content
=
content
.
replaceFirst
(
content
=
content
.
replaceFirst
(
'
\n
dependencies:
\n
'
,
'
\n
dependencies:
\n
'
,
// One dynamic framework, one static framework, and one that does not support iOS.
// One dynamic framework, one static framework, one Dart-only,
'
\n
dependencies:
\n
device_info: 2.0.3
\n
google_sign_in: 4.5.1
\n
android_alarm_manager: 0.4.5+11
\n
'
,
// and one that does not support iOS.
'''
dependencies:
device_info: 2.0.3
google_sign_in: 4.5.1
android_alarm_manager: 0.4.5+11
$dartPluginName
:
path: ../
$dartPluginName
'''
,
);
);
await
pubspec
.
writeAsString
(
content
,
flush:
true
);
await
pubspec
.
writeAsString
(
content
,
flush:
true
);
await
inDirectory
(
projectDir
,
()
async
{
await
inDirectory
(
projectDir
,
()
async
{
...
@@ -191,7 +205,8 @@ Future<void> main() async {
...
@@ -191,7 +205,8 @@ Future<void> main() async {
||
!
podfileLockOutput
.
contains
(
':path: Flutter/FlutterPluginRegistrant'
)
||
!
podfileLockOutput
.
contains
(
':path: Flutter/FlutterPluginRegistrant'
)
||
!
podfileLockOutput
.
contains
(
':path: ".symlinks/plugins/device_info/ios"'
)
||
!
podfileLockOutput
.
contains
(
':path: ".symlinks/plugins/device_info/ios"'
)
||
!
podfileLockOutput
.
contains
(
':path: ".symlinks/plugins/google_sign_in/ios"'
)
||
!
podfileLockOutput
.
contains
(
':path: ".symlinks/plugins/google_sign_in/ios"'
)
||
podfileLockOutput
.
contains
(
'android_alarm_manager'
))
{
||
podfileLockOutput
.
contains
(
'android_alarm_manager'
)
||
podfileLockOutput
.
contains
(
dartPluginName
))
{
print
(
podfileLockOutput
);
print
(
podfileLockOutput
);
return
TaskResult
.
failure
(
'Building ephemeral host app Podfile.lock does not contain expected pods'
);
return
TaskResult
.
failure
(
'Building ephemeral host app Podfile.lock does not contain expected pods'
);
}
}
...
@@ -205,6 +220,9 @@ Future<void> main() async {
...
@@ -205,6 +220,9 @@ Future<void> main() async {
// Android-only, no embedded framework.
// Android-only, no embedded framework.
checkDirectoryNotExists
(
path
.
join
(
ephemeralIOSHostApp
.
path
,
'Frameworks'
,
'android_alarm_manager.framework'
));
checkDirectoryNotExists
(
path
.
join
(
ephemeralIOSHostApp
.
path
,
'Frameworks'
,
'android_alarm_manager.framework'
));
// Dart-only, no embedded framework.
checkDirectoryNotExists
(
path
.
join
(
ephemeralIOSHostApp
.
path
,
'Frameworks'
,
'
$dartPluginName
.framework'
));
section
(
'Clean and pub get module'
);
section
(
'Clean and pub get module'
);
await
inDirectory
(
projectDir
,
()
async
{
await
inDirectory
(
projectDir
,
()
async
{
...
@@ -243,7 +261,8 @@ Future<void> main() async {
...
@@ -243,7 +261,8 @@ Future<void> main() async {
||
!
hostPodfileLockOutput
.
contains
(
':path: "../hello/.ios/Flutter/FlutterPluginRegistrant"'
)
||
!
hostPodfileLockOutput
.
contains
(
':path: "../hello/.ios/Flutter/FlutterPluginRegistrant"'
)
||
!
hostPodfileLockOutput
.
contains
(
':path: "../hello/.ios/.symlinks/plugins/device_info/ios"'
)
||
!
hostPodfileLockOutput
.
contains
(
':path: "../hello/.ios/.symlinks/plugins/device_info/ios"'
)
||
!
hostPodfileLockOutput
.
contains
(
':path: "../hello/.ios/.symlinks/plugins/google_sign_in/ios"'
)
||
!
hostPodfileLockOutput
.
contains
(
':path: "../hello/.ios/.symlinks/plugins/google_sign_in/ios"'
)
||
hostPodfileLockOutput
.
contains
(
'android_alarm_manager'
))
{
||
hostPodfileLockOutput
.
contains
(
'android_alarm_manager'
)
||
hostPodfileLockOutput
.
contains
(
dartPluginName
))
{
print
(
hostPodfileLockOutput
);
print
(
hostPodfileLockOutput
);
throw
TaskResult
.
failure
(
'Building host app Podfile.lock does not contain expected pods'
);
throw
TaskResult
.
failure
(
'Building host app Podfile.lock does not contain expected pods'
);
}
}
...
@@ -501,3 +520,46 @@ Future<bool> _isAppAotBuild(Directory app) async {
...
@@ -501,3 +520,46 @@ Future<bool> _isAppAotBuild(Directory app) async {
return
symbolTable
.
contains
(
'kDartIsolateSnapshotInstructions'
);
return
symbolTable
.
contains
(
'kDartIsolateSnapshotInstructions'
);
}
}
Future
<
void
>
_createFakeDartPlugin
(
String
name
,
Directory
parent
)
async
{
// Start from a standard plugin template.
await
inDirectory
(
parent
,
()
async
{
await
flutter
(
'create'
,
options:
<
String
>[
'--org'
,
'io.flutter.devicelab'
,
'--template=plugin'
,
'--platforms=ios'
,
name
,
],
);
});
final
String
pluginDir
=
path
.
join
(
parent
.
path
,
name
);
// Convert the metadata to Dart-only.
final
String
dartPluginClass
=
'DartClassFor
$name
'
;
final
File
pubspec
=
File
(
path
.
join
(
pluginDir
,
'pubspec.yaml'
));
String
content
=
await
pubspec
.
readAsString
();
content
=
content
.
replaceAll
(
RegExp
(
r' pluginClass: .*?\n'
),
' dartPluginClass:
$dartPluginClass
\n
'
,
);
await
pubspec
.
writeAsString
(
content
,
flush:
true
);
// Add the Dart registration hook that the build will generate a call to.
final
File
dartCode
=
File
(
path
.
join
(
pluginDir
,
'lib'
,
'
$name
.dart'
));
content
=
await
dartCode
.
readAsString
();
content
=
'''
$content
class
$dartPluginClass
{
static void registerWith() {}
}
'''
;
await
dartCode
.
writeAsString
(
content
,
flush:
true
);
// Remove the native plugin code.
await
Directory
(
path
.
join
(
pluginDir
,
'ios'
)).
delete
(
recursive:
true
);
}
dev/devicelab/bin/tasks/plugin_test.dart
View file @
2df7dca8
...
@@ -16,5 +16,7 @@ Future<void> main() async {
...
@@ -16,5 +16,7 @@ Future<void> main() async {
<
String
,
String
>{
'ENABLE_ANDROID_EMBEDDING_V2'
:
'true'
}),
<
String
,
String
>{
'ENABLE_ANDROID_EMBEDDING_V2'
:
'true'
}),
PluginTest
(
'apk'
,
<
String
>[
'-a'
,
'kotlin'
,
'--platforms=android'
],
pluginCreateEnvironment:
PluginTest
(
'apk'
,
<
String
>[
'-a'
,
'kotlin'
,
'--platforms=android'
],
pluginCreateEnvironment:
<
String
,
String
>{
'ENABLE_ANDROID_EMBEDDING_V2'
:
'true'
}),
<
String
,
String
>{
'ENABLE_ANDROID_EMBEDDING_V2'
:
'true'
}),
// Test that Dart-only plugins are supported.
PluginTest
(
'apk'
,
<
String
>[
'--platforms=android'
],
dartOnlyPlugin:
true
),
]));
]));
}
}
dev/devicelab/bin/tasks/plugin_test_ios.dart
View file @
2df7dca8
...
@@ -10,5 +10,8 @@ Future<void> main() async {
...
@@ -10,5 +10,8 @@ Future<void> main() async {
PluginTest
(
'ios'
,
<
String
>[
'-i'
,
'objc'
,
'--platforms=ios'
]),
PluginTest
(
'ios'
,
<
String
>[
'-i'
,
'objc'
,
'--platforms=ios'
]),
PluginTest
(
'ios'
,
<
String
>[
'-i'
,
'swift'
,
'--platforms=ios'
]),
PluginTest
(
'ios'
,
<
String
>[
'-i'
,
'swift'
,
'--platforms=ios'
]),
PluginTest
(
'macos'
,
<
String
>[
'--platforms=macos'
]),
PluginTest
(
'macos'
,
<
String
>[
'--platforms=macos'
]),
// Test that Dart-only plugins are supported.
PluginTest
(
'ios'
,
<
String
>[
'--platforms=ios'
],
dartOnlyPlugin:
true
),
PluginTest
(
'macos'
,
<
String
>[
'--platforms=macos'
],
dartOnlyPlugin:
true
),
]));
]));
}
}
dev/devicelab/lib/tasks/plugin_tests.dart
View file @
2df7dca8
...
@@ -26,12 +26,19 @@ TaskFunction combine(List<TaskFunction> tasks) {
...
@@ -26,12 +26,19 @@ TaskFunction combine(List<TaskFunction> tasks) {
/// Defines task that creates new Flutter project, adds a local and remote
/// Defines task that creates new Flutter project, adds a local and remote
/// plugin, and then builds the specified [buildTarget].
/// plugin, and then builds the specified [buildTarget].
class
PluginTest
{
class
PluginTest
{
PluginTest
(
this
.
buildTarget
,
this
.
options
,
{
this
.
pluginCreateEnvironment
,
this
.
appCreateEnvironment
});
PluginTest
(
this
.
buildTarget
,
this
.
options
,
{
this
.
pluginCreateEnvironment
,
this
.
appCreateEnvironment
,
this
.
dartOnlyPlugin
=
false
,
});
final
String
buildTarget
;
final
String
buildTarget
;
final
List
<
String
>
options
;
final
List
<
String
>
options
;
final
Map
<
String
,
String
>?
pluginCreateEnvironment
;
final
Map
<
String
,
String
>?
pluginCreateEnvironment
;
final
Map
<
String
,
String
>?
appCreateEnvironment
;
final
Map
<
String
,
String
>?
appCreateEnvironment
;
final
bool
dartOnlyPlugin
;
Future
<
TaskResult
>
call
()
async
{
Future
<
TaskResult
>
call
()
async
{
final
Directory
tempDir
=
final
Directory
tempDir
=
...
@@ -41,6 +48,9 @@ class PluginTest {
...
@@ -41,6 +48,9 @@ class PluginTest {
final
_FlutterProject
plugin
=
await
_FlutterProject
.
create
(
final
_FlutterProject
plugin
=
await
_FlutterProject
.
create
(
tempDir
,
options
,
buildTarget
,
tempDir
,
options
,
buildTarget
,
name:
'plugintest'
,
template:
'plugin'
,
environment:
pluginCreateEnvironment
);
name:
'plugintest'
,
template:
'plugin'
,
environment:
pluginCreateEnvironment
);
if
(
dartOnlyPlugin
)
{
await
plugin
.
convertDefaultPluginToDartPlugin
();
}
section
(
'Test plugin'
);
section
(
'Test plugin'
);
await
plugin
.
test
();
await
plugin
.
test
();
section
(
'Create Flutter app'
);
section
(
'Create Flutter app'
);
...
@@ -52,7 +62,7 @@ class PluginTest {
...
@@ -52,7 +62,7 @@ class PluginTest {
pluginPath:
path
.
join
(
'..'
,
'plugintest'
));
pluginPath:
path
.
join
(
'..'
,
'plugintest'
));
await
app
.
addPlugin
(
'path_provider'
);
await
app
.
addPlugin
(
'path_provider'
);
section
(
'Build app'
);
section
(
'Build app'
);
await
app
.
build
(
buildTarget
);
await
app
.
build
(
buildTarget
,
validateNativeBuildProject:
!
dartOnlyPlugin
);
section
(
'Test app'
);
section
(
'Test app'
);
await
app
.
test
();
await
app
.
test
();
}
finally
{
}
finally
{
...
@@ -76,8 +86,10 @@ class _FlutterProject {
...
@@ -76,8 +86,10 @@ class _FlutterProject {
String
get
rootPath
=>
path
.
join
(
parent
.
path
,
name
);
String
get
rootPath
=>
path
.
join
(
parent
.
path
,
name
);
File
get
pubspecFile
=>
File
(
path
.
join
(
rootPath
,
'pubspec.yaml'
));
Future
<
void
>
addPlugin
(
String
plugin
,
{
String
?
pluginPath
})
async
{
Future
<
void
>
addPlugin
(
String
plugin
,
{
String
?
pluginPath
})
async
{
final
File
pubspec
=
File
(
path
.
join
(
rootPath
,
'pubspec.yaml'
))
;
final
File
pubspec
=
pubspecFile
;
String
content
=
await
pubspec
.
readAsString
();
String
content
=
await
pubspec
.
readAsString
();
final
String
dependency
=
final
String
dependency
=
pluginPath
!=
null
?
'
$plugin
:
\n
path:
$pluginPath
'
:
'
$plugin
:'
;
pluginPath
!=
null
?
'
$plugin
:
\n
path:
$pluginPath
'
:
'
$plugin
:'
;
...
@@ -88,6 +100,47 @@ class _FlutterProject {
...
@@ -88,6 +100,47 @@ class _FlutterProject {
await
pubspec
.
writeAsString
(
content
,
flush:
true
);
await
pubspec
.
writeAsString
(
content
,
flush:
true
);
}
}
/// Converts a plugin created from the standard template to a Dart-only
/// plugin.
Future
<
void
>
convertDefaultPluginToDartPlugin
()
async
{
final
String
dartPluginClass
=
'DartClassFor
$name
'
;
// Convert the metadata.
final
File
pubspec
=
pubspecFile
;
String
content
=
await
pubspec
.
readAsString
();
content
=
content
.
replaceAll
(
RegExp
(
r' pluginClass: .*?\n'
),
' dartPluginClass:
$dartPluginClass
\n
'
,
);
await
pubspec
.
writeAsString
(
content
,
flush:
true
);
// Add the Dart registration hook that the build will generate a call to.
final
File
dartCode
=
File
(
path
.
join
(
rootPath
,
'lib'
,
'
$name
.dart'
));
content
=
await
dartCode
.
readAsString
();
content
=
'''
$content
class
$dartPluginClass
{
static void registerWith() {}
}
'''
;
await
dartCode
.
writeAsString
(
content
,
flush:
true
);
// Remove any native plugin code.
const
List
<
String
>
platforms
=
<
String
>[
'android'
,
'ios'
,
'linux'
,
'macos'
,
'windows'
,
];
for
(
final
String
platform
in
platforms
)
{
final
Directory
platformDir
=
Directory
(
path
.
join
(
rootPath
,
platform
));
if
(
platformDir
.
existsSync
())
{
await
platformDir
.
delete
(
recursive:
true
);
}
}
}
Future
<
void
>
test
()
async
{
Future
<
void
>
test
()
async
{
await
inDirectory
(
Directory
(
rootPath
),
()
async
{
await
inDirectory
(
Directory
(
rootPath
),
()
async
{
await
flutter
(
'test'
);
await
flutter
(
'test'
);
...
@@ -147,7 +200,7 @@ class _FlutterProject {
...
@@ -147,7 +200,7 @@ class _FlutterProject {
podspec
.
writeAsStringSync
(
podspecContent
,
flush:
true
);
podspec
.
writeAsStringSync
(
podspecContent
,
flush:
true
);
}
}
Future
<
void
>
build
(
String
target
)
async
{
Future
<
void
>
build
(
String
target
,
{
bool
validateNativeBuildProject
=
true
}
)
async
{
await
inDirectory
(
Directory
(
rootPath
),
()
async
{
await
inDirectory
(
Directory
(
rootPath
),
()
async
{
final
String
buildOutput
=
await
evalFlutter
(
'build'
,
options:
<
String
>[
final
String
buildOutput
=
await
evalFlutter
(
'build'
,
options:
<
String
>[
target
,
target
,
...
@@ -167,28 +220,30 @@ class _FlutterProject {
...
@@ -167,28 +220,30 @@ class _FlutterProject {
throw
TaskResult
.
failure
(
'Minimum plugin version warning present'
);
throw
TaskResult
.
failure
(
'Minimum plugin version warning present'
);
}
}
final
File
podsProject
=
File
(
path
.
join
(
rootPath
,
target
,
'Pods'
,
'Pods.xcodeproj'
,
'project.pbxproj'
));
if
(
validateNativeBuildProject
)
{
if
(!
podsProject
.
existsSync
())
{
final
File
podsProject
=
File
(
path
.
join
(
rootPath
,
target
,
'Pods'
,
'Pods.xcodeproj'
,
'project.pbxproj'
));
throw
TaskResult
.
failure
(
'Xcode Pods project file missing at
${podsProject.path}
'
);
if
(!
podsProject
.
existsSync
())
{
}
throw
TaskResult
.
failure
(
'Xcode Pods project file missing at
${podsProject.path}
'
);
final
String
podsProjectContent
=
podsProject
.
readAsStringSync
();
if
(
target
==
'ios'
)
{
// Plugins with versions lower than the app version should not have IPHONEOS_DEPLOYMENT_TARGET set.
// The plugintest plugin target should not have IPHONEOS_DEPLOYMENT_TARGET set since it has been lowered
// in _reduceDarwinPluginMinimumVersion to 7, which is below the target version of 9.
if
(
podsProjectContent
.
contains
(
'IPHONEOS_DEPLOYMENT_TARGET = 7'
))
{
throw
TaskResult
.
failure
(
'Plugin build setting IPHONEOS_DEPLOYMENT_TARGET not removed'
);
}
}
if
(!
podsProjectContent
.
contains
(
r'"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited) i386";'
))
{
throw
TaskResult
.
failure
(
r'EXCLUDED_ARCHS is not "$(inherited) i386"'
);
final
String
podsProjectContent
=
podsProject
.
readAsStringSync
();
if
(
target
==
'ios'
)
{
// Plugins with versions lower than the app version should not have IPHONEOS_DEPLOYMENT_TARGET set.
// The plugintest plugin target should not have IPHONEOS_DEPLOYMENT_TARGET set since it has been lowered
// in _reduceDarwinPluginMinimumVersion to 7, which is below the target version of 9.
if
(
podsProjectContent
.
contains
(
'IPHONEOS_DEPLOYMENT_TARGET = 7'
))
{
throw
TaskResult
.
failure
(
'Plugin build setting IPHONEOS_DEPLOYMENT_TARGET not removed'
);
}
if
(!
podsProjectContent
.
contains
(
r'"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "$(inherited) i386";'
))
{
throw
TaskResult
.
failure
(
r'EXCLUDED_ARCHS is not "$(inherited) i386"'
);
}
}
}
}
// Same for macOS deployment target, but 10.8.
// Same for macOS deployment target, but 10.8.
// The plugintest target should not have MACOSX_DEPLOYMENT_TARGET set.
// The plugintest target should not have MACOSX_DEPLOYMENT_TARGET set.
if
(
target
==
'macos'
&&
podsProjectContent
.
contains
(
'MACOSX_DEPLOYMENT_TARGET = 10.8'
))
{
if
(
target
==
'macos'
&&
podsProjectContent
.
contains
(
'MACOSX_DEPLOYMENT_TARGET = 10.8'
))
{
throw
TaskResult
.
failure
(
'Plugin build setting MACOSX_DEPLOYMENT_TARGET not removed'
);
throw
TaskResult
.
failure
(
'Plugin build setting MACOSX_DEPLOYMENT_TARGET not removed'
);
}
}
}
}
}
});
});
...
...
packages/flutter_tools/bin/podhelper.rb
View file @
2df7dca8
...
@@ -255,7 +255,8 @@ def flutter_install_plugin_pods(application_path = nil, relative_symlink_dir, pl
...
@@ -255,7 +255,8 @@ def flutter_install_plugin_pods(application_path = nil, relative_symlink_dir, pl
plugin_pods
.
each
do
|
plugin_hash
|
plugin_pods
.
each
do
|
plugin_hash
|
plugin_name
=
plugin_hash
[
'name'
]
plugin_name
=
plugin_hash
[
'name'
]
plugin_path
=
plugin_hash
[
'path'
]
plugin_path
=
plugin_hash
[
'path'
]
if
(
plugin_name
&&
plugin_path
)
has_native_build
=
plugin_hash
.
fetch
(
'native_build'
,
true
)
if
(
plugin_name
&&
plugin_path
&&
has_native_build
)
symlink
=
File
.
join
(
symlink_plugins_dir
,
plugin_name
)
symlink
=
File
.
join
(
symlink_plugins_dir
,
plugin_name
)
File
.
symlink
(
plugin_path
,
symlink
)
File
.
symlink
(
plugin_path
,
symlink
)
...
...
packages/flutter_tools/gradle/app_plugin_loader.gradle
View file @
2df7dca8
...
@@ -23,6 +23,12 @@ assert object.plugins.android instanceof List
...
@@ -23,6 +23,12 @@ assert object.plugins.android instanceof List
object
.
plugins
.
android
.
each
{
androidPlugin
->
object
.
plugins
.
android
.
each
{
androidPlugin
->
assert
androidPlugin
.
name
instanceof
String
assert
androidPlugin
.
name
instanceof
String
assert
androidPlugin
.
path
instanceof
String
assert
androidPlugin
.
path
instanceof
String
// Skip plugins that have no native build (such as a Dart-only implementation
// of a federated plugin).
def
needsBuild
=
androidPlugin
.
containsKey
(
'native_build'
)
?
androidPlugin
[
'native_build'
]
:
true
if
(!
needsBuild
)
{
return
}
def
pluginDirectory
=
new
File
(
androidPlugin
.
path
,
'android'
)
def
pluginDirectory
=
new
File
(
androidPlugin
.
path
,
'android'
)
assert
pluginDirectory
.
exists
()
assert
pluginDirectory
.
exists
()
include
":${androidPlugin.name}"
include
":${androidPlugin.name}"
...
...
packages/flutter_tools/gradle/module_plugin_loader.gradle
View file @
2df7dca8
...
@@ -20,6 +20,12 @@ if (pluginsFile.exists()) {
...
@@ -20,6 +20,12 @@ if (pluginsFile.exists()) {
object
.
plugins
.
android
.
each
{
androidPlugin
->
object
.
plugins
.
android
.
each
{
androidPlugin
->
assert
androidPlugin
.
name
instanceof
String
assert
androidPlugin
.
name
instanceof
String
assert
androidPlugin
.
path
instanceof
String
assert
androidPlugin
.
path
instanceof
String
// Skip plugins that have no native build (such as a Dart-only
// implementation of a federated plugin).
def
needsBuild
=
androidPlugin
.
containsKey
(
'native_build'
)
?
androidPlugin
[
'native_build'
]
:
true
if
(!
needsBuild
)
{
return
}
def
pluginDirectory
=
new
File
(
androidPlugin
.
path
,
'android'
)
def
pluginDirectory
=
new
File
(
androidPlugin
.
path
,
'android'
)
assert
pluginDirectory
.
exists
()
assert
pluginDirectory
.
exists
()
include
":${androidPlugin.name}"
include
":${androidPlugin.name}"
...
...
packages/flutter_tools/lib/src/flutter_plugins.dart
View file @
2df7dca8
...
@@ -98,6 +98,7 @@ const String _kFlutterPluginsPluginListKey = 'plugins';
...
@@ -98,6 +98,7 @@ const String _kFlutterPluginsPluginListKey = 'plugins';
const
String
_kFlutterPluginsNameKey
=
'name'
;
const
String
_kFlutterPluginsNameKey
=
'name'
;
const
String
_kFlutterPluginsPathKey
=
'path'
;
const
String
_kFlutterPluginsPathKey
=
'path'
;
const
String
_kFlutterPluginsDependenciesKey
=
'dependencies'
;
const
String
_kFlutterPluginsDependenciesKey
=
'dependencies'
;
const
String
_kFlutterPluginsHasNativeBuildKey
=
'native_build'
;
/// Filters [plugins] to those supported by [platformKey].
/// Filters [plugins] to those supported by [platformKey].
List
<
Map
<
String
,
Object
>>
_filterPluginsByPlatform
(
List
<
Plugin
>
plugins
,
String
platformKey
)
{
List
<
Map
<
String
,
Object
>>
_filterPluginsByPlatform
(
List
<
Plugin
>
plugins
,
String
platformKey
)
{
...
@@ -108,9 +109,13 @@ List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String
...
@@ -108,9 +109,13 @@ List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String
final
Set
<
String
>
pluginNames
=
platformPlugins
.
map
((
Plugin
plugin
)
=>
plugin
.
name
).
toSet
();
final
Set
<
String
>
pluginNames
=
platformPlugins
.
map
((
Plugin
plugin
)
=>
plugin
.
name
).
toSet
();
final
List
<
Map
<
String
,
Object
>>
pluginInfo
=
<
Map
<
String
,
Object
>>[];
final
List
<
Map
<
String
,
Object
>>
pluginInfo
=
<
Map
<
String
,
Object
>>[];
for
(
final
Plugin
plugin
in
platformPlugins
)
{
for
(
final
Plugin
plugin
in
platformPlugins
)
{
// This is guaranteed to be non-null due to the `where` filter above.
final
PluginPlatform
platformPlugin
=
plugin
.
platforms
[
platformKey
]!;
pluginInfo
.
add
(<
String
,
Object
>{
pluginInfo
.
add
(<
String
,
Object
>{
_kFlutterPluginsNameKey:
plugin
.
name
,
_kFlutterPluginsNameKey:
plugin
.
name
,
_kFlutterPluginsPathKey:
globals
.
fsUtils
.
escapePath
(
plugin
.
path
),
_kFlutterPluginsPathKey:
globals
.
fsUtils
.
escapePath
(
plugin
.
path
),
if
(
platformPlugin
is
NativeOrDartPlugin
)
_kFlutterPluginsHasNativeBuildKey:
(
platformPlugin
as
NativeOrDartPlugin
).
isNative
(),
_kFlutterPluginsDependenciesKey:
<
String
>[...
plugin
.
dependencies
.
where
(
pluginNames
.
contains
)],
_kFlutterPluginsDependenciesKey:
<
String
>[...
plugin
.
dependencies
.
where
(
pluginNames
.
contains
)],
});
});
}
}
...
@@ -130,7 +135,8 @@ List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String
...
@@ -130,7 +135,8 @@ List<Map<String, Object>> _filterPluginsByPlatform(List<Plugin> plugins, String
/// "dependencies": [
/// "dependencies": [
/// "plugin-a",
/// "plugin-a",
/// "plugin-b"
/// "plugin-b"
/// ]
/// ],
/// "native_build": true
/// }
/// }
/// ],
/// ],
/// "android": [],
/// "android": [],
...
...
packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb.tmpl
View file @
2df7dca8
...
@@ -81,7 +81,8 @@ def install_flutter_plugin_pods(flutter_application_path)
...
@@ -81,7 +81,8 @@ def install_flutter_plugin_pods(flutter_application_path)
plugin_pods.each do |plugin_hash|
plugin_pods.each do |plugin_hash|
plugin_name = plugin_hash['name']
plugin_name = plugin_hash['name']
plugin_path = plugin_hash['path']
plugin_path = plugin_hash['path']
if (plugin_name && plugin_path)
has_native_build = plugin_hash.fetch('native_build', true)
if (plugin_name && plugin_path && has_native_build)
symlink = File.join(symlinks_dir, plugin_name)
symlink = File.join(symlinks_dir, plugin_name)
FileUtils.rm_f(symlink)
FileUtils.rm_f(symlink)
File.symlink(plugin_path, symlink)
File.symlink(plugin_path, symlink)
...
...
packages/flutter_tools/test/general.shard/plugins_test.dart
View file @
2df7dca8
...
@@ -30,6 +30,46 @@ import '../src/context.dart';
...
@@ -30,6 +30,46 @@ import '../src/context.dart';
import
'../src/fakes.dart'
hide
FakeOperatingSystemUtils
;
import
'../src/fakes.dart'
hide
FakeOperatingSystemUtils
;
import
'../src/pubspec_schema.dart'
;
import
'../src/pubspec_schema.dart'
;
/// Information for a platform entry in the 'platforms' section of a plugin's
/// pubspec.yaml.
class
_PluginPlatformInfo
{
const
_PluginPlatformInfo
({
this
.
pluginClass
,
this
.
dartPluginClass
,
this
.
androidPackage
,
this
.
fileName
})
:
assert
(
pluginClass
!=
null
||
dartPluginClass
!=
null
),
assert
(
androidPackage
==
null
||
pluginClass
!=
null
);
/// The pluginClass entry, if any.
final
String
pluginClass
;
/// The dartPluginClass entry, if any.
final
String
dartPluginClass
;
/// The package entry for an Android plugin implementation using pluginClass.
final
String
androidPackage
;
/// The fileName entry for a web plugin implementation.
final
String
fileName
;
/// Returns the body of a platform section for a plugin's pubspec, properly
/// indented.
String
get
indentedPubspecSection
{
const
String
indentation
=
' '
;
return
<
String
>[
if
(
pluginClass
!=
null
)
'
${indentation}
pluginClass:
$pluginClass
'
,
if
(
dartPluginClass
!=
null
)
'
${indentation}
dartPluginClass:
$dartPluginClass
'
,
if
(
androidPackage
!=
null
)
'
${indentation}
package:
$androidPackage
'
,
if
(
fileName
!=
null
)
'
${indentation}
fileName:
$fileName
'
,
].
join
(
'
\n
'
);
}
}
void
main
(
)
{
void
main
(
)
{
group
(
'plugins'
,
()
{
group
(
'plugins'
,
()
{
FileSystem
fs
;
FileSystem
fs
;
...
@@ -331,7 +371,7 @@ flutter:
...
@@ -331,7 +371,7 @@ flutter:
);
);
}
}
Directory
createPluginWithDependencies
({
Directory
create
Legacy
PluginWithDependencies
({
@required
String
name
,
@required
String
name
,
@required
List
<
String
>
dependencies
,
@required
List
<
String
>
dependencies
,
})
{
})
{
...
@@ -347,6 +387,44 @@ flutter:
...
@@ -347,6 +387,44 @@ flutter:
plugin:
plugin:
androidPackage: plugin2
androidPackage: plugin2
pluginClass: UseNewEmbedding
pluginClass: UseNewEmbedding
dependencies:
'''
);
for
(
final
String
dependency
in
dependencies
)
{
pluginDirectory
.
childFile
(
'pubspec.yaml'
)
.
writeAsStringSync
(
'
$dependency
:
\n
'
,
mode:
FileMode
.
append
);
}
flutterProject
.
directory
.
childFile
(
'.packages'
)
.
writeAsStringSync
(
'
$name
:
${pluginDirectory.childDirectory('lib').uri.toString()}
\n
'
,
mode:
FileMode
.
append
,
);
return
pluginDirectory
;
}
Directory
createPlugin
({
@required
String
name
,
@required
Map
<
String
,
_PluginPlatformInfo
>
platforms
,
List
<
String
>
dependencies
=
const
<
String
>[],
})
{
assert
(
name
!=
null
);
assert
(
dependencies
!=
null
);
final
Iterable
<
String
>
platformSections
=
platforms
.
entries
.
map
((
MapEntry
<
String
,
_PluginPlatformInfo
>
entry
)
=>
'''
${entry.key}
:
${entry.value.indentedPubspecSection}
'''
);
final
Directory
pluginDirectory
=
fs
.
systemTempDirectory
.
createTempSync
(
'flutter_plugin.'
);
pluginDirectory
.
childFile
(
'pubspec.yaml'
)
.
writeAsStringSync
(
'''
name:
$name
flutter:
plugin:
platforms:
${platformSections.join('\n')}
dependencies:
dependencies:
'''
);
'''
);
for
(
final
String
dependency
in
dependencies
)
{
for
(
final
String
dependency
in
dependencies
)
{
...
@@ -420,9 +498,9 @@ dependencies:
...
@@ -420,9 +498,9 @@ dependencies:
testUsingContext
(
testUsingContext
(
'Refreshing the plugin list modifies .flutter-plugins '
'Refreshing the plugin list modifies .flutter-plugins '
'and .flutter-plugins-dependencies when there are plugins'
,
()
async
{
'and .flutter-plugins-dependencies when there are plugins'
,
()
async
{
final
Directory
pluginA
=
createPluginWithDependencies
(
name:
'plugin-a'
,
dependencies:
const
<
String
>[
'plugin-b'
,
'plugin-c'
,
'random-package'
]);
final
Directory
pluginA
=
create
Legacy
PluginWithDependencies
(
name:
'plugin-a'
,
dependencies:
const
<
String
>[
'plugin-b'
,
'plugin-c'
,
'random-package'
]);
final
Directory
pluginB
=
createPluginWithDependencies
(
name:
'plugin-b'
,
dependencies:
const
<
String
>[
'plugin-c'
]);
final
Directory
pluginB
=
create
Legacy
PluginWithDependencies
(
name:
'plugin-b'
,
dependencies:
const
<
String
>[
'plugin-c'
]);
final
Directory
pluginC
=
createPluginWithDependencies
(
name:
'plugin-c'
,
dependencies:
const
<
String
>[]);
final
Directory
pluginC
=
create
Legacy
PluginWithDependencies
(
name:
'plugin-c'
,
dependencies:
const
<
String
>[]);
iosProject
.
testExists
=
true
;
iosProject
.
testExists
=
true
;
final
DateTime
dateCreated
=
DateTime
(
1970
);
final
DateTime
dateCreated
=
DateTime
(
1970
);
...
@@ -449,22 +527,25 @@ dependencies:
...
@@ -449,22 +527,25 @@ dependencies:
<
String
,
dynamic
>
{
<
String
,
dynamic
>
{
'name'
:
'plugin-a'
,
'name'
:
'plugin-a'
,
'path'
:
'
${pluginA.path}
/'
,
'path'
:
'
${pluginA.path}
/'
,
'native_build'
:
true
,
'dependencies'
:
<
String
>[
'dependencies'
:
<
String
>[
'plugin-b'
,
'plugin-b'
,
'plugin-c'
'plugin-c'
]
]
,
},
},
<
String
,
dynamic
>
{
<
String
,
dynamic
>
{
'name'
:
'plugin-b'
,
'name'
:
'plugin-b'
,
'path'
:
'
${pluginB.path}
/'
,
'path'
:
'
${pluginB.path}
/'
,
'native_build'
:
true
,
'dependencies'
:
<
String
>[
'dependencies'
:
<
String
>[
'plugin-c'
'plugin-c'
]
]
,
},
},
<
String
,
dynamic
>
{
<
String
,
dynamic
>
{
'name'
:
'plugin-c'
,
'name'
:
'plugin-c'
,
'path'
:
'
${pluginC.path}
/'
,
'path'
:
'
${pluginC.path}
/'
,
'dependencies'
:
<
String
>[]
'native_build'
:
true
,
'dependencies'
:
<
String
>[],
},
},
];
];
expect
(
plugins
[
'ios'
],
expectedPlugins
);
expect
(
plugins
[
'ios'
],
expectedPlugins
);
...
@@ -514,6 +595,51 @@ dependencies:
...
@@ -514,6 +595,51 @@ dependencies:
FlutterVersion:
()
=>
flutterVersion
FlutterVersion:
()
=>
flutterVersion
});
});
testUsingContext
(
'.flutter-plugins-dependencies indicates native build inclusion'
,
()
async
{
createPlugin
(
name:
'plugin-a'
,
platforms:
const
<
String
,
_PluginPlatformInfo
>{
// Native-only; should include native build.
'android'
:
_PluginPlatformInfo
(
pluginClass:
'Foo'
,
androidPackage:
'bar.foo'
),
// Hybrid native and Dart; should include native build.
'ios'
:
_PluginPlatformInfo
(
pluginClass:
'Foo'
,
dartPluginClass:
'Bar'
),
// Web; should not have the native build key at all since it doesn't apply.
'web'
:
_PluginPlatformInfo
(
pluginClass:
'Foo'
,
fileName:
'lib/foo.dart'
),
// Dart-only; should not include native build.
'windows'
:
_PluginPlatformInfo
(
dartPluginClass:
'Foo'
),
});
iosProject
.
testExists
=
true
;
final
DateTime
dateCreated
=
DateTime
(
1970
);
systemClock
.
currentTime
=
dateCreated
;
await
refreshPluginsList
(
flutterProject
);
expect
(
flutterProject
.
flutterPluginsDependenciesFile
.
existsSync
(),
true
);
final
String
pluginsString
=
flutterProject
.
flutterPluginsDependenciesFile
.
readAsStringSync
();
final
Map
<
String
,
dynamic
>
jsonContent
=
json
.
decode
(
pluginsString
)
as
Map
<
String
,
dynamic
>;
final
Map
<
String
,
dynamic
>
plugins
=
jsonContent
[
'plugins'
]
as
Map
<
String
,
dynamic
>;
// Extracts the native_build key (if any) from the first plugin for the
// given platform.
bool
getNativeBuildValue
(
String
platform
)
{
final
List
<
Map
<
String
,
dynamic
>>
platformPlugins
=
(
plugins
[
platform
]
as
List
<
dynamic
>).
cast
<
Map
<
String
,
dynamic
>>();
expect
(
platformPlugins
.
length
,
1
);
return
platformPlugins
[
0
][
'native_build'
]
as
bool
;
}
expect
(
getNativeBuildValue
(
'android'
),
true
);
expect
(
getNativeBuildValue
(
'ios'
),
true
);
expect
(
getNativeBuildValue
(
'web'
),
null
);
expect
(
getNativeBuildValue
(
'windows'
),
false
);
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fs
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
SystemClock:
()
=>
systemClock
,
FlutterVersion:
()
=>
flutterVersion
});
testUsingContext
(
'Changes to the plugin list invalidates the Cocoapod lockfiles'
,
()
async
{
testUsingContext
(
'Changes to the plugin list invalidates the Cocoapod lockfiles'
,
()
async
{
simulatePodInstallRun
(
iosProject
);
simulatePodInstallRun
(
iosProject
);
simulatePodInstallRun
(
macosProject
);
simulatePodInstallRun
(
macosProject
);
...
...
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