diff --git a/lib/commands/create.dart b/lib/commands/create.dart index 1504a32..9d3fb55 100644 --- a/lib/commands/create.dart +++ b/lib/commands/create.dart @@ -115,16 +115,6 @@ class ELinuxCreateCommand extends CreateCommand { try { for (final Directory projectType in eLinuxTemplates.listSync().whereType()) { - final Directory sourceRunnerCommon = - projectType.childDirectory('runner'); - if (!sourceRunnerCommon.existsSync()) { - continue; - } - final Directory sourceFlutter = projectType.childDirectory('flutter'); - if (!sourceFlutter.existsSync()) { - continue; - } - final Directory dest = templates .childDirectory(projectType.basename) .childDirectory('elinux.tmpl'); @@ -133,8 +123,19 @@ class ELinuxCreateCommand extends CreateCommand { } copyDirectory(projectType, dest); - copyDirectory(sourceFlutter, dest.childDirectory('flutter')); - copyDirectory(sourceRunnerCommon, dest.childDirectory('runner')); + if (projectType.basename == 'app') { + final Directory sourceRunnerCommon = + projectType.childDirectory('runner'); + if (!sourceRunnerCommon.existsSync()) { + continue; + } + final Directory sourceFlutter = projectType.childDirectory('flutter'); + if (!sourceFlutter.existsSync()) { + continue; + } + copyDirectory(sourceFlutter, dest.childDirectory('flutter')); + copyDirectory(sourceRunnerCommon, dest.childDirectory('runner')); + } created.add(dest); } return await runInternal(); diff --git a/lib/elinux_build_target.dart b/lib/elinux_build_target.dart index 6da7b7d..f09c7b2 100644 --- a/lib/elinux_build_target.dart +++ b/lib/elinux_build_target.dart @@ -344,7 +344,7 @@ class NativeBundle { } writeGeneratedCmakeConfig(Cache.flutterRoot, eLinuxProject, environment); - //createPluginSymlinks(eLinuxProject.parent); + createPluginSymlinks(eLinuxProject.parent); } // Run the native build. diff --git a/lib/elinux_plugins.dart b/lib/elinux_plugins.dart index 05cbd7e..c600c76 100644 --- a/lib/elinux_plugins.dart +++ b/lib/elinux_plugins.dart @@ -6,6 +6,8 @@ // @dart = 2.8 +import 'dart:convert'; + import 'package:file/file.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/terminal.dart'; @@ -13,6 +15,7 @@ import 'package:flutter_tools/src/build_system/targets/web.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/dart/language_version.dart'; import 'package:flutter_tools/src/dart/package_map.dart'; +import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/flutter_plugins.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/platform_plugins.dart'; @@ -460,3 +463,77 @@ void _renderTemplateToFile(String template, dynamic context, String filePath) { file.createSync(recursive: true); file.writeAsStringSync(renderedTemplate); } + +// Key strings for the .flutter-plugins-dependencies file. +const String _kFlutterPluginsPluginListKey = 'plugins'; +const String _kFlutterPluginsNameKey = 'name'; +const String _kFlutterPluginsPathKey = 'path'; + +/// For each platform that uses them, creates symlinks within the platform +/// directory to each plugin used on that platform. +/// +/// If |force| is true, the symlinks will be recreated, otherwise they will +/// be created only if missing. +/// +/// This uses [project.flutterPluginsDependenciesFile], so it should only be +/// run after refreshPluginList has been run since the last plugin change. +void createPluginSymlinks(FlutterProject project, + {bool force = false, + @visibleForTesting FeatureFlags featureFlagsOverride}) { + Map platformPlugins; + final String pluginFileContent = + _readFileContent(project.flutterPluginsDependenciesFile); + + if (pluginFileContent != null) { + final Map pluginInfo = + json.decode(pluginFileContent) as Map; + platformPlugins = + pluginInfo[_kFlutterPluginsPluginListKey] as Map; + } + platformPlugins ??= {}; + + final ELinuxProject eLinuxProject = ELinuxProject.fromFlutter(project); + if (eLinuxProject.existsSync()) { + _createPlatformPluginSymlinks( + eLinuxProject.pluginSymlinkDirectory, + platformPlugins[eLinuxProject.pluginConfigKey] as List, + force: force, + ); + } +} + +/// Returns the contents of [File] or [null] if that file does not exist. +String _readFileContent(File file) { + return file.existsSync() ? file.readAsStringSync() : null; +} + +/// Creates [symlinkDirectory] containing symlinks to each plugin listed in [platformPlugins]. +/// +/// If [force] is true, the directory will be created only if missing. +void _createPlatformPluginSymlinks( + Directory symlinkDirectory, List platformPlugins, + {bool force = false}) { + if (force && symlinkDirectory.existsSync()) { + // Start fresh to avoid stale links. + symlinkDirectory.deleteSync(recursive: true); + } + symlinkDirectory.createSync(recursive: true); + if (platformPlugins == null) { + return; + } + for (final Map pluginInfo + in platformPlugins.cast>()) { + final String name = pluginInfo[_kFlutterPluginsNameKey] as String; + final String path = pluginInfo[_kFlutterPluginsPathKey] as String; + final Link link = symlinkDirectory.childLink(name); + if (link.existsSync()) { + continue; + } + try { + link.createSync(path); + } on FileSystemException catch (e) { + handleSymlinkException(e, platform: globals.platform, os: globals.os); + rethrow; + } + } +} diff --git a/templates/plugin/.gitignore b/templates/plugin/.gitignore new file mode 100644 index 0000000..f68e1be --- /dev/null +++ b/templates/plugin/.gitignore @@ -0,0 +1 @@ +flutter/ diff --git a/templates/plugin/CMakeLists.txt.tmpl b/templates/plugin/CMakeLists.txt.tmpl new file mode 100644 index 0000000..d6e662a --- /dev/null +++ b/templates/plugin/CMakeLists.txt.tmpl @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.15) +set(PROJECT_NAME "{{projectName}}") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "{{projectName}}_plugin") + +add_library(${PLUGIN_NAME} SHARED + "{{pluginClassSnakeCase}}.cc" +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin +set({{projectName}}_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/templates/plugin/include/projectName.tmpl/pluginClassSnakeCase.h.tmpl b/templates/plugin/include/projectName.tmpl/pluginClassSnakeCase.h.tmpl new file mode 100644 index 0000000..68c1a61 --- /dev/null +++ b/templates/plugin/include/projectName.tmpl/pluginClassSnakeCase.h.tmpl @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_{{pluginClassCapitalSnakeCase}}_H_ +#define FLUTTER_PLUGIN_{{pluginClassCapitalSnakeCase}}_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void {{pluginClass}}RegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_{{pluginClassCapitalSnakeCase}}_H_ diff --git a/templates/plugin/pluginClassSnakeCase.cc.tmpl b/templates/plugin/pluginClassSnakeCase.cc.tmpl new file mode 100644 index 0000000..c2e4aa9 --- /dev/null +++ b/templates/plugin/pluginClassSnakeCase.cc.tmpl @@ -0,0 +1,69 @@ +#include "include/{{projectName}}/{{pluginClassSnakeCase}}.h" + +#include +#include +#include + +#include +#include +#include + +namespace { + +class {{pluginClass}} : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar); + + {{pluginClass}}(); + + virtual ~{{pluginClass}}(); + + private: + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); +}; + +// static +void {{pluginClass}}::RegisterWithRegistrar( + flutter::PluginRegistrar *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "{{projectName}}", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique<{{pluginClass}}>(); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +{{pluginClass}}::{{pluginClass}}() {} + +{{pluginClass}}::~{{pluginClass}}() {} + +void {{pluginClass}}::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("getPlatformVersion") == 0) { + std::ostringstream version_stream; + version_stream << "eLinux"; + result->Success(flutter::EncodableValue(version_stream.str())); + } else { + result->NotImplemented(); + } +} + +} // namespace + +void {{pluginClass}}RegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + {{pluginClass}}::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/templates/template_manifest.json b/templates/template_manifest.json index 5306a63..4d5f231 100644 --- a/templates/template_manifest.json +++ b/templates/template_manifest.json @@ -11,6 +11,10 @@ "templates/app/elinux.tmpl/runner/flutter_window.cc", "templates/app/elinux.tmpl/runner/flutter_window.h", "templates/app/elinux.tmpl/runner/main.cc", + "templates/plugin/elinux.tmpl/.gitignore", + "templates/plugin/elinux.tmpl/CMakeLists.txt.tmpl", + "templates/plugin/elinux.tmpl/include/projectName.tmpl/pluginClassSnakeCase.h.tmpl", + "templates/plugin/elinux.tmpl/pluginClassSnakeCase.cc.tmpl", "templates/app/.gitignore.tmpl", "templates/app/.idea/libraries/Dart_SDK.xml.tmpl",