未验证 提交 6eacdce8 编写于 作者: S stuartmorgan 提交者: GitHub

[windows] Allow engine flags via environment vars (#21161)

Replaces the (temporary) compile-time option to pass engine switches
with the ability to pass them temporarily at runtime via environment
variables.

This is enabled only for debug/profile to avoid potential issues with
tampering with released applicaitons, but if there is a need for that in
the future it could be added (potentially with a whitelist, as is
currently used for Dart VM flags).

Windows portion of:
https://github.com/flutter/flutter/issues/38569
https://github.com/flutter/flutter/issues/60393
上级 84d50d71
......@@ -1360,6 +1360,7 @@ FILE: ../../../flutter/shell/platform/windows/cursor_handler.cc
FILE: ../../../flutter/shell/platform/windows/cursor_handler.h
FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.cc
FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.h
FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle_unittests.cc
FILE: ../../../flutter/shell/platform/windows/flutter_windows.cc
FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.cc
FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.h
......
......@@ -121,6 +121,7 @@ executable("flutter_windows_unittests") {
testonly = true
sources = [
"flutter_project_bundle_unittests.cc",
"string_conversion_unittests.cc",
"system_utils_unittests.cc",
"testing/mock_win32_window.cc",
......
......@@ -26,12 +26,6 @@ class DartProjectTest : public ::testing::Test {
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) {
return project.engine_switches();
}
};
TEST_F(DartProjectTest, StandardProjectFormat) {
......@@ -41,12 +35,4 @@ TEST_F(DartProjectTest, StandardProjectFormat) {
EXPECT_EQ(GetProjectAotLibraryPath(project), L"test\\app.so");
}
TEST_F(DartProjectTest, Switches) {
DartProject project(L"test");
std::vector<std::string> switches = {"--foo", "--bar"};
project.SetEngineSwitches(switches);
EXPECT_EQ(GetProjectEngineSwitches(project).size(), 2);
EXPECT_EQ(GetProjectEngineSwitches(project)[0], "--foo");
}
} // namespace flutter
......@@ -16,15 +16,6 @@ FlutterEngine::FlutterEngine(const DartProject& project) {
c_engine_properties.assets_path = project.assets_path().c_str();
c_engine_properties.icu_data_path = project.icu_data_path().c_str();
c_engine_properties.aot_library_path = project.aot_library_path().c_str();
std::vector<const char*> engine_switches;
std::transform(
project.engine_switches().begin(), project.engine_switches().end(),
std::back_inserter(engine_switches),
[](const std::string& arg) -> const char* { return arg.c_str(); });
if (engine_switches.size() > 0) {
c_engine_properties.switches = &engine_switches[0];
c_engine_properties.switches_count = engine_switches.size();
}
engine_ = FlutterDesktopEngineCreate(c_engine_properties);
......
......@@ -29,17 +29,6 @@ class DartProject {
~DartProject() = default;
// Switches to pass to the Flutter engine. See
// https://github.com/flutter/engine/blob/master/shell/common/switches.h
// for details. Not all switches will apply to embedding mode. Switches have
// not stability guarantee, and are subject to change without notice.
//
// Note: This WILL BE REMOVED in the future. If you call this, please see
// https://github.com/flutter/flutter/issues/38569.
void SetEngineSwitches(const std::vector<std::string>& switches) {
engine_switches_ = switches;
}
private:
// Accessors for internals are private, so that they can be changed if more
// flexible options for project structures are needed later without it
......@@ -52,9 +41,6 @@ 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_;
}
// The path to the assets directory.
std::wstring assets_path_;
......@@ -63,8 +49,6 @@ class DartProject {
// 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_;
};
} // namespace flutter
......
......@@ -4,8 +4,10 @@
#include "flutter/shell/platform/windows/flutter_project_bundle.h"
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <sstream>
#include "flutter/shell/platform/common/cpp/path_utils.h"
......@@ -38,11 +40,6 @@ FlutterProjectBundle::FlutterProjectBundle(
}
}
}
if (properties.switches_count > 0) {
switches_.insert(switches_.end(), &properties.switches[0],
&properties.switches[properties.switches_count]);
}
}
bool FlutterProjectBundle::HasValidPaths() {
......@@ -78,4 +75,30 @@ UniqueAotDataPtr FlutterProjectBundle::LoadAotData() {
FlutterProjectBundle::~FlutterProjectBundle() {}
const std::vector<std::string> FlutterProjectBundle::GetSwitches() {
std::vector<std::string> switches;
// Read engine switches from the environment in debug/profile.
#ifndef FLUTTER_RELEASE
const char* switch_count_key = "FLUTTER_ENGINE_SWITCHES";
const int kMaxSwitchCount = 50;
const char* switch_count_string = std::getenv(switch_count_key);
int switch_count = std::min(
kMaxSwitchCount, switch_count_string ? atoi(switch_count_string) : 0);
for (int i = 1; i <= switch_count; ++i) {
std::ostringstream switch_key;
switch_key << "FLUTTER_ENGINE_SWITCH_" << i;
const char* switch_value = std::getenv(switch_key.str().c_str());
if (switch_value) {
std::ostringstream switch_value_as_flag;
switch_value_as_flag << "--" << switch_value;
switches.push_back(switch_value_as_flag.str());
} else {
std::cerr << switch_count << " keys expected from " << switch_count_key
<< ", but " << switch_key.str() << " is missing." << std::endl;
}
}
#endif // !FLUTTER_RELEASE
return switches;
}
} // namespace flutter
......@@ -43,7 +43,7 @@ class FlutterProjectBundle {
const std::filesystem::path& icu_path() { return icu_path_; }
// Returns any switches that should be passed to the engine.
const std::vector<std::string>& switches() { return switches_; }
const std::vector<std::string> GetSwitches();
// Attempts to load AOT data for this bundle. The returned data must be
// retained until any engine instance it is passed to has been shut down.
......@@ -54,7 +54,6 @@ class FlutterProjectBundle {
private:
std::filesystem::path assets_path_;
std::filesystem::path icu_path_;
std::vector<std::string> switches_;
// Path to the AOT library file, if any.
std::filesystem::path aot_library_path_;
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/flutter_project_bundle.h"
#include "gtest/gtest.h"
namespace flutter {
namespace testing {
TEST(FlutterProjectBundle, BasicPropertiesAbsolutePaths) {
FlutterDesktopEngineProperties properties = {};
properties.assets_path = L"C:\\foo\\flutter_assets";
properties.icu_data_path = L"C:\\foo\\icudtl.dat";
FlutterProjectBundle project(properties);
EXPECT_TRUE(project.HasValidPaths());
EXPECT_EQ(project.assets_path().string(), "C:\\foo\\flutter_assets");
EXPECT_EQ(project.icu_path().string(), "C:\\foo\\icudtl.dat");
}
TEST(FlutterProjectBundle, BasicPropertiesRelativePaths) {
FlutterDesktopEngineProperties properties = {};
properties.assets_path = L"foo\\flutter_assets";
properties.icu_data_path = L"foo\\icudtl.dat";
FlutterProjectBundle project(properties);
EXPECT_TRUE(project.HasValidPaths());
EXPECT_TRUE(project.assets_path().is_absolute());
EXPECT_EQ(project.assets_path().filename().string(), "flutter_assets");
EXPECT_TRUE(project.icu_path().is_absolute());
EXPECT_EQ(project.icu_path().filename().string(), "icudtl.dat");
}
TEST(FlutterProjectBundle, SwitchesEmpty) {
FlutterDesktopEngineProperties properties = {};
properties.assets_path = L"foo\\flutter_assets";
properties.icu_data_path = L"foo\\icudtl.dat";
// Clear the main environment variable, since test order is not guaranteed.
_putenv_s("FLUTTER_ENGINE_SWITCHES", "");
FlutterProjectBundle project(properties);
EXPECT_EQ(project.GetSwitches().size(), 0);
}
#ifndef FLUTTER_RELEASE
TEST(FlutterProjectBundle, Switches) {
FlutterDesktopEngineProperties properties = {};
properties.assets_path = L"foo\\flutter_assets";
properties.icu_data_path = L"foo\\icudtl.dat";
_putenv_s("FLUTTER_ENGINE_SWITCHES", "2");
_putenv_s("FLUTTER_ENGINE_SWITCH_1", "abc");
_putenv_s("FLUTTER_ENGINE_SWITCH_2", "foo=\"bar, baz\"");
FlutterProjectBundle project(properties);
std::vector<std::string> switches = project.GetSwitches();
EXPECT_EQ(switches.size(), 2);
EXPECT_EQ(switches[0], "--abc");
EXPECT_EQ(switches[1], "--foo=\"bar, baz\"");
}
TEST(FlutterProjectBundle, SwitchesExtraValues) {
FlutterDesktopEngineProperties properties = {};
properties.assets_path = L"foo\\flutter_assets";
properties.icu_data_path = L"foo\\icudtl.dat";
_putenv_s("FLUTTER_ENGINE_SWITCHES", "1");
_putenv_s("FLUTTER_ENGINE_SWITCH_1", "abc");
_putenv_s("FLUTTER_ENGINE_SWITCH_2", "foo=\"bar, baz\"");
FlutterProjectBundle project(properties);
std::vector<std::string> switches = project.GetSwitches();
EXPECT_EQ(switches.size(), 1);
EXPECT_EQ(switches[0], "--abc");
}
TEST(FlutterProjectBundle, SwitchesMissingValues) {
FlutterDesktopEngineProperties properties = {};
properties.assets_path = L"foo\\flutter_assets";
properties.icu_data_path = L"foo\\icudtl.dat";
_putenv_s("FLUTTER_ENGINE_SWITCHES", "4");
_putenv_s("FLUTTER_ENGINE_SWITCH_1", "abc");
_putenv_s("FLUTTER_ENGINE_SWITCH_2", "foo=\"bar, baz\"");
_putenv_s("FLUTTER_ENGINE_SWITCH_4", "oops");
FlutterProjectBundle project(properties);
std::vector<std::string> switches = project.GetSwitches();
EXPECT_EQ(switches.size(), 3);
EXPECT_EQ(switches[0], "--abc");
EXPECT_EQ(switches[1], "--foo=\"bar, baz\"");
// The missing switch should be skipped, leaving SWITCH_4 as the third
// switch in the array.
EXPECT_EQ(switches[2], "--oops");
}
#endif // !FLUTTER_RELEASE
} // namespace testing
} // namespace flutter
......@@ -137,10 +137,10 @@ bool FlutterWindowsEngine::RunWithEntrypoint(const char* entrypoint) {
// FlutterProjectArgs is expecting a full argv, so when processing it for
// flags the first item is treated as the executable and ignored. Add a dummy
// value so that all provided arguments are used.
std::vector<std::string> switches = project_->GetSwitches();
std::vector<const char*> argv = {"placeholder"};
std::transform(
project_->switches().begin(), project_->switches().end(),
std::back_inserter(argv),
switches.begin(), switches.end(), std::back_inserter(argv),
[](const std::string& arg) -> const char* { return arg.c_str(); });
// Configure task runners.
......
......@@ -46,15 +46,6 @@ typedef struct {
// 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
// for details. Not all arguments will apply to desktop.
const char** switches;
// The number of elements in |switches|.
size_t switches_count;
} FlutterDesktopEngineProperties;
// ========== View Controller ==========
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册