mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Support AOT mode for Windows (#18589)
- Adds a way to provide an AOT library to the C API. - Adds app.so to the information provided by DartProject. - Fixes the engine to only do static snapshot linking for Windows in debug mode, not all modes, so that the provided library is used. Engine side of https://github.com/flutter/flutter/issues/38477
This commit is contained in:
parent
1a8349888e
commit
903ef0892f
@ -89,28 +89,6 @@ source_set("runtime") {
|
||||
"//third_party/skia",
|
||||
]
|
||||
|
||||
# On Windows the engine finds the Dart snapshot data through symbols
|
||||
# that are statically linked into the executable. On other platforms this
|
||||
# data is obtained by a dynamic symbol lookup.
|
||||
# The current Windows platform uses static-link instead of dynamic lookups.
|
||||
# Because:
|
||||
# 1. The size of the executable on Windows is not as concerned as on mobile
|
||||
# platforms.
|
||||
# 2. If it is a dynamic lookup method, you need to modify the properties of
|
||||
# the memory page after the *.bin is dynamically loaded at runtime and
|
||||
# mark it as executable. This is feasible (similar to V8 on Windows), but
|
||||
# this increases the complexity of the implementation(These operations
|
||||
# involve the use of some system file/memory privilege APIs and there is
|
||||
# a risk of failure).
|
||||
# 3. In the executable can enjoy the advantages of the windows preload
|
||||
# mechanism, speed up I/O.
|
||||
if (is_win) {
|
||||
if (flutter_runtime_mode == "profile" ||
|
||||
flutter_runtime_mode == "release") {
|
||||
deps += [ "//flutter/lib/snapshot" ]
|
||||
}
|
||||
}
|
||||
|
||||
public_deps = [
|
||||
"//third_party/rapidjson",
|
||||
]
|
||||
|
||||
@ -24,7 +24,7 @@ const char* DartSnapshot::kIsolateInstructionsSymbol =
|
||||
// data through symbols that are statically linked into the executable.
|
||||
// On other platforms this data is obtained by a dynamic symbol lookup.
|
||||
#define DART_SNAPSHOT_STATIC_LINK \
|
||||
(OS_WIN || (OS_ANDROID && FLUTTER_JIT_RUNTIME))
|
||||
((OS_WIN || OS_ANDROID) && FLUTTER_JIT_RUNTIME)
|
||||
|
||||
#if !DART_SNAPSHOT_STATIC_LINK
|
||||
|
||||
|
||||
@ -22,6 +22,11 @@ class DartProjectTest : public ::testing::Test {
|
||||
return project.assets_path();
|
||||
}
|
||||
|
||||
// Wrapper for accessing private aot_library_path_.
|
||||
std::wstring GetProjectAotLibraryPath(const DartProject& project) {
|
||||
return project.aot_library_path();
|
||||
}
|
||||
|
||||
// Wrapper for accessing private engine_switches.
|
||||
std::vector<std::string> GetProjectEngineSwitches(
|
||||
const DartProject& project) {
|
||||
@ -33,6 +38,7 @@ TEST_F(DartProjectTest, StandardProjectFormat) {
|
||||
DartProject project(L"test");
|
||||
EXPECT_EQ(GetProjectIcuDataPath(project), L"test\\icudtl.dat");
|
||||
EXPECT_EQ(GetProjectAssetsPath(project), L"test\\flutter_assets");
|
||||
EXPECT_EQ(GetProjectAotLibraryPath(project), L"test\\app.so");
|
||||
}
|
||||
|
||||
TEST_F(DartProjectTest, Switches) {
|
||||
|
||||
@ -22,6 +22,8 @@ FlutterViewController::FlutterViewController(int width,
|
||||
FlutterDesktopEngineProperties properties = {};
|
||||
properties.assets_path = project.assets_path().c_str();
|
||||
properties.icu_data_path = project.icu_data_path().c_str();
|
||||
// It is harmless to pass this in non-AOT mode.
|
||||
properties.aot_library_path = project.aot_library_path().c_str();
|
||||
properties.switches = switch_count > 0 ? switches.data() : nullptr;
|
||||
properties.switches_count = switch_count;
|
||||
controller_ = FlutterDesktopCreateViewController(width, height, properties);
|
||||
|
||||
@ -17,12 +17,14 @@ class DartProject {
|
||||
// the following top-level items:
|
||||
// - icudtl.dat (provided as a resource by the Flutter tool)
|
||||
// - flutter_assets (as built by the Flutter tool)
|
||||
// - app.so, for an AOT build (as built by the Flutter tool)
|
||||
//
|
||||
// The path can either be absolute, or relative to the directory containing
|
||||
// the running executable.
|
||||
explicit DartProject(const std::wstring& path) {
|
||||
assets_path_ = path + L"\\flutter_assets";
|
||||
icu_data_path_ = path + L"\\icudtl.dat";
|
||||
aot_library_path_ = path + L"\\app.so";
|
||||
}
|
||||
|
||||
~DartProject() = default;
|
||||
@ -48,6 +50,7 @@ class DartProject {
|
||||
|
||||
const std::wstring& assets_path() const { return assets_path_; }
|
||||
const std::wstring& icu_data_path() const { return icu_data_path_; }
|
||||
const std::wstring& aot_library_path() const { return aot_library_path_; }
|
||||
const std::vector<std::string>& engine_switches() const {
|
||||
return engine_switches_;
|
||||
}
|
||||
@ -56,6 +59,9 @@ class DartProject {
|
||||
std::wstring assets_path_;
|
||||
// The path to the ICU data.
|
||||
std::wstring icu_data_path_;
|
||||
// The path to the AOT library. This will always return a path, but non-AOT
|
||||
// builds will not be expected to actually have a library at that path.
|
||||
std::wstring aot_library_path_;
|
||||
// Switches to pass to the engine.
|
||||
std::vector<std::string> engine_switches_;
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h"
|
||||
@ -29,6 +30,33 @@
|
||||
|
||||
static_assert(FLUTTER_ENGINE_VERSION == 1, "");
|
||||
|
||||
// Attempts to load AOT data from the given path, which must be absolute and
|
||||
// non-empty. Logs and returns nullptr on failure.
|
||||
UniqueAotDataPtr LoadAotData(std::filesystem::path aot_data_path) {
|
||||
if (aot_data_path.empty()) {
|
||||
std::cerr
|
||||
<< "Attempted to load AOT data, but no aot_library_path was provided."
|
||||
<< std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
if (!std::filesystem::exists(aot_data_path)) {
|
||||
std::cerr << "Can't load AOT data from " << aot_data_path.u8string()
|
||||
<< "; no such file." << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
std::string path_string = aot_data_path.u8string();
|
||||
FlutterEngineAOTDataSource source = {};
|
||||
source.type = kFlutterEngineAOTDataSourceTypeElfPath;
|
||||
source.elf_path = path_string.c_str();
|
||||
FlutterEngineAOTData data = nullptr;
|
||||
auto result = FlutterEngineCreateAOTData(&source, &data);
|
||||
if (result != kSuccess) {
|
||||
std::cerr << "Failed to load AOT data from: " << path_string << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
return UniqueAotDataPtr(data);
|
||||
}
|
||||
|
||||
// Spins up an instance of the Flutter Engine.
|
||||
//
|
||||
// This function launches the Flutter Engine in a background thread, supplying
|
||||
@ -108,7 +136,12 @@ static std::unique_ptr<FlutterDesktopEngineState> RunFlutterEngine(
|
||||
|
||||
std::filesystem::path assets_path(engine_properties.assets_path);
|
||||
std::filesystem::path icu_path(engine_properties.icu_data_path);
|
||||
if (assets_path.is_relative() || icu_path.is_relative()) {
|
||||
std::filesystem::path aot_library_path =
|
||||
engine_properties.aot_library_path == nullptr
|
||||
? std::filesystem::path()
|
||||
: std::filesystem::path(engine_properties.aot_library_path);
|
||||
if (assets_path.is_relative() || icu_path.is_relative() ||
|
||||
(!aot_library_path.empty() && aot_library_path.is_relative())) {
|
||||
// Treat relative paths as relative to the directory of this executable.
|
||||
std::filesystem::path executable_location =
|
||||
flutter::GetExecutableDirectory();
|
||||
@ -120,10 +153,22 @@ static std::unique_ptr<FlutterDesktopEngineState> RunFlutterEngine(
|
||||
}
|
||||
assets_path = std::filesystem::path(executable_location) / assets_path;
|
||||
icu_path = std::filesystem::path(executable_location) / icu_path;
|
||||
if (!aot_library_path.empty()) {
|
||||
aot_library_path =
|
||||
std::filesystem::path(executable_location) / aot_library_path;
|
||||
}
|
||||
}
|
||||
std::string assets_path_string = assets_path.u8string();
|
||||
std::string icu_path_string = icu_path.u8string();
|
||||
|
||||
if (FlutterEngineRunsAOTCompiledDartCode()) {
|
||||
state->aot_data = LoadAotData(aot_library_path);
|
||||
if (!state->aot_data) {
|
||||
std::cerr << "Unable to start engine without AOT data." << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FlutterProjectArgs args = {};
|
||||
args.struct_size = sizeof(FlutterProjectArgs);
|
||||
args.assets_path = assets_path_string.c_str();
|
||||
@ -137,6 +182,9 @@ static std::unique_ptr<FlutterDesktopEngineState> RunFlutterEngine(
|
||||
return window->HandlePlatformMessage(engine_message);
|
||||
};
|
||||
args.custom_task_runners = &custom_task_runners;
|
||||
if (state->aot_data) {
|
||||
args.aot_data = state->aot_data.get();
|
||||
}
|
||||
|
||||
FLUTTER_API_SYMBOL(FlutterEngine) engine = nullptr;
|
||||
auto result =
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_WINDOWS_PUBLIC_FLUTTER_H_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@ -12,8 +13,6 @@
|
||||
#include "flutter_messenger.h"
|
||||
#include "flutter_plugin_registrar.h"
|
||||
|
||||
#include "Windows.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -40,6 +39,12 @@ typedef struct {
|
||||
// containing the executable.
|
||||
const wchar_t* icu_data_path;
|
||||
|
||||
// The path to the AOT libary file for your application, if any.
|
||||
// This can either be an absolute path or a path relative to the directory
|
||||
// containing the executable. This can be nullptr for a non-AOT build, as
|
||||
// it will be ignored in that case.
|
||||
const wchar_t* aot_library_path;
|
||||
|
||||
// The switches to pass to the Flutter engine.
|
||||
//
|
||||
// See: https://github.com/flutter/engine/blob/master/shell/common/switches.h
|
||||
|
||||
@ -39,6 +39,14 @@ struct FlutterDesktopView {
|
||||
flutter::Win32FlutterWindow* window;
|
||||
};
|
||||
|
||||
struct AotDataDeleter {
|
||||
void operator()(FlutterEngineAOTData aot_data) {
|
||||
FlutterEngineCollectAOTData(aot_data);
|
||||
}
|
||||
};
|
||||
|
||||
using UniqueAotDataPtr = std::unique_ptr<_FlutterEngineAOTData, AotDataDeleter>;
|
||||
|
||||
// Struct for storing state of a Flutter engine instance.
|
||||
struct FlutterDesktopEngineState {
|
||||
// The handle to the Flutter engine instance.
|
||||
@ -46,6 +54,9 @@ struct FlutterDesktopEngineState {
|
||||
|
||||
// Task runner for tasks posted from the engine.
|
||||
std::unique_ptr<flutter::Win32TaskRunner> task_runner;
|
||||
|
||||
// AOT data, if any.
|
||||
UniqueAotDataPtr aot_data;
|
||||
};
|
||||
|
||||
// State associated with the plugin registrar.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user