Unverified Commit dd2756c3 authored by stuartmorgan's avatar stuartmorgan Committed by GitHub

Generate a makefile for Linux plugins (#51520)

When generating the plugin registrant for Linux, also generate a
makefile that can be included in the app-level Makefile to manage all of
the plugin targets and flags, exporting them in a few known variables
for use in the outer makefile.

Part of #32720
parent 3b5668c9
......@@ -801,6 +801,40 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
}
''';
const String _linuxPluginMakefileTemplate = '''
# Plugins to include in the build.
GENERATED_PLUGINS=\\
{{#plugins}}
\t{{name}} \\
{{/plugins}}
GENERATED_PLUGINS_DIR={{pluginsDir}}
# A plugin library name plugin name with _plugin appended.
GENERATED_PLUGIN_LIB_NAMES=\$(foreach plugin,\$(GENERATED_PLUGINS),\$(plugin)_plugin)
# Variables for use in the enclosing Makefile. Changes to these names are
# breaking changes.
PLUGIN_TARGETS=\$(GENERATED_PLUGINS)
PLUGIN_LIBRARIES=\$(foreach plugin,\$(GENERATED_PLUGIN_LIB_NAMES),\\
\t\$(OUT_DIR)/lib\$(plugin).so)
PLUGIN_LDFLAGS=\$(patsubst %,-l%,\$(GENERATED_PLUGIN_LIB_NAMES))
PLUGIN_CPPFLAGS=\$(foreach plugin,\$(GENERATED_PLUGINS),\\
\t-I\$(GENERATED_PLUGINS_DIR)/\$(plugin)/linux)
# Targets
# Implicit rules don't match phony targets, so list plugin builds explicitly.
{{#plugins}}
\$(OUT_DIR)/lib{{name}}_plugin.so: | {{name}}
{{/plugins}}
.PHONY: \$(GENERATED_PLUGINS)
\$(GENERATED_PLUGINS):
make -C \$(GENERATED_PLUGINS_DIR)/\$@/linux \\
OUT_DIR=\$(OUT_DIR) \\
FLUTTER_EPHEMERAL_DIR="\$(abspath {{ephemeralDir}})"
''';
Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
final List<Map<String, dynamic>> iosPlugins = _extractPlatformMaps(plugins, IOSPlugin.kConfigKey);
final Map<String, dynamic> context = <String, dynamic>{
......@@ -841,12 +875,34 @@ Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plug
}
}
Future<void> _writeLinuxPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
Future<void> _writeLinuxPluginFiles(FlutterProject project, List<Plugin> plugins) async {
final List<Map<String, dynamic>> linuxPlugins = _extractPlatformMaps(plugins, LinuxPlugin.kConfigKey);
// The generated makefile is checked in, so can't use absolute paths. It is
// included by the main makefile, so relative paths must be relative to that
// file's directory.
final String makefileDirPath = project.linux.makeFile.parent.absolute.path;
final Map<String, dynamic> context = <String, dynamic>{
'plugins': linuxPlugins,
'ephemeralDir': globals.fs.path.relative(
project.linux.ephemeralDirectory.absolute.path,
from: makefileDirPath,
),
'pluginsDir': globals.fs.path.relative(
project.linux.pluginSymlinkDirectory.absolute.path,
from: makefileDirPath,
),
};
await _writeCppPluginRegistrant(project.linux.managedDirectory, context);
await _writeLinuxPluginMakefile(project.linux.managedDirectory, context);
}
Future<void> _writeLinuxPluginMakefile(Directory destination, Map<String, dynamic> templateContext) async {
final String registryDirectory = destination.path;
_renderTemplateToFile(
_linuxPluginMakefileTemplate,
templateContext,
globals.fs.path.join(registryDirectory, 'generated_plugins.mk'),
);
}
Future<void> _writeMacOSPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
......@@ -1034,7 +1090,7 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false})
// desktop in existing projects are in place. For now, ignore checkProjects
// on desktop and always treat it as true.
if (featureFlags.isLinuxEnabled && project.linux.existsSync()) {
await _writeLinuxPluginRegistrant(project, plugins);
await _writeLinuxPluginFiles(project, plugins);
}
if (featureFlags.isMacOSEnabled && project.macos.existsSync()) {
await _writeMacOSPluginRegistrant(project, plugins);
......
......@@ -1003,6 +1003,9 @@ class LinuxProject extends FlutterProjectPlatform {
/// the build.
File get generatedMakeConfigFile => ephemeralDirectory.childFile('generated_config.mk');
/// Makefile with rules and variables for plugin builds.
File get generatedPluginMakeFile => managedDirectory.childFile('generated_plugins.mk');
/// The directory to write plugin symlinks.
Directory get pluginSymlinkDirectory => ephemeralDirectory.childDirectory('.plugin_symlinks');
......
......@@ -83,7 +83,13 @@ void main() {
linuxProject = MockLinuxProject();
when(flutterProject.linux).thenReturn(linuxProject);
when(linuxProject.pluginConfigKey).thenReturn('linux');
when(linuxProject.pluginSymlinkDirectory).thenReturn(flutterProject.directory.childDirectory('linux').childDirectory('symlinks'));
final Directory linuxManagedDirectory = flutterProject.directory.childDirectory('linux').childDirectory('flutter');
final Directory linuxEphemeralDirectory = linuxManagedDirectory.childDirectory('ephemeral');
when(linuxProject.managedDirectory).thenReturn(linuxManagedDirectory);
when(linuxProject.ephemeralDirectory).thenReturn(linuxEphemeralDirectory);
when(linuxProject.pluginSymlinkDirectory).thenReturn(linuxEphemeralDirectory.childDirectory('.plugin_symlinks'));
when(linuxProject.makeFile).thenReturn(linuxManagedDirectory.parent.childFile('Makefile'));
when(linuxProject.generatedPluginMakeFile).thenReturn(linuxManagedDirectory.childFile('generated_plugins.mk'));
when(linuxProject.existsSync()).thenReturn(false);
when(mockClock.now()).thenAnswer(
......@@ -871,6 +877,51 @@ web_plugin_with_nested:${webPluginWithNestedFile.childDirectory('lib').uri.toStr
FeatureFlags: () => featureFlags,
});
testUsingContext('Injecting creates generated Linux registrant', () async {
when(linuxProject.existsSync()).thenReturn(true);
when(featureFlags.isLinuxEnabled).thenReturn(true);
when(flutterProject.isModule).thenReturn(false);
configureDummyPackageAsPlugin();
await injectPlugins(flutterProject, checkProjects: true);
final File registrantHeader = linuxProject.managedDirectory.childFile('generated_plugin_registrant.h');
final File registrantImpl = linuxProject.managedDirectory.childFile('generated_plugin_registrant.cc');
expect(registrantHeader.existsSync(), isTrue);
expect(registrantImpl.existsSync(), isTrue);
expect(registrantImpl.readAsStringSync(), contains('SomePluginRegisterWithRegistrar'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => featureFlags,
});
testUsingContext('Injecting creates generated Linux plugin makefile', () async {
when(linuxProject.existsSync()).thenReturn(true);
when(featureFlags.isLinuxEnabled).thenReturn(true);
when(flutterProject.isModule).thenReturn(false);
configureDummyPackageAsPlugin();
await injectPlugins(flutterProject, checkProjects: true);
final File pluginMakefile = linuxProject.generatedPluginMakeFile;
expect(pluginMakefile.existsSync(), isTrue);
final String contents = pluginMakefile.readAsStringSync();
expect(contents, contains('libapackage_plugin.so'));
// Verify all the variables the app-level Makefile rely on.
expect(contents, contains('PLUGIN_TARGETS='));
expect(contents, contains('PLUGIN_LIBRARIES='));
expect(contents, contains('PLUGIN_LDFLAGS='));
expect(contents, contains('PLUGIN_CPPFLAGS='));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => featureFlags,
});
testUsingContext('Injecting creates generated Windows registrant', () async {
when(windowsProject.existsSync()).thenReturn(true);
when(featureFlags.isWindowsEnabled).thenReturn(true);
......
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