Skip to content

Commit

Permalink
[Windows] Set Platform.executable on engine start (flutter#35560)
Browse files Browse the repository at this point in the history
When setting FlutterProjectArgs.command_line_argv prior to launching the
engine, we were previously setting a placeholder value rather than the
executable name. This resulted in Platform.executable (from dart:io)
returning "placeholder" in application code.

This updates the Windows implementation for consistency with macOS and
guarantees that Platform.executable will return a reasonable value in
Dart code.

Note that this does not affect Platform.resolvedExecutable, which returns
a full, resolved path, and is implemented in the Dart runtime itself.
Previously the code:

   print(Platform.executable);
   print(Platform.resolvedExecutable);

resulted in the following output on Windows:

   flutter: placeholder
   flutter: C:\path\to\project\build\windows\runner\Debug\project.exe

after this patch, it results in:

   flutter: project.exe
   flutter: C:\path\to\project\build\windows\runner\Debug\project.exe

Issue: flutter/flutter#83921
  • Loading branch information
cbracken authored Aug 20, 2022
1 parent a9783b2 commit 7fb8311
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 5 deletions.
9 changes: 9 additions & 0 deletions shell/platform/windows/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io' as io;
import 'dart:ui' as ui;

// Signals a waiting latch in the native test.
Expand All @@ -10,6 +11,9 @@ void signal() native 'Signal';
// Signals a waiting latch in the native test, passing a boolean value.
void signalBoolValue(bool value) native 'SignalBoolValue';

// Signals a waiting latch in the native test, passing a string value.
void signalStringValue(String value) native 'SignalStringValue';

// Signals a waiting latch in the native test, which returns a value to the fixture.
bool signalBoolReturn() native 'SignalBoolReturn';

Expand Down Expand Up @@ -39,6 +43,11 @@ void verifyNativeFunctionWithReturn() {
signalBoolValue(value);
}

@pragma('vm:entry-point')
void readPlatformExecutable() {
signalStringValue(io.Platform.executable);
}

@pragma('vm:entry-point')
void drawHelloWorld() {
ui.PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
Expand Down
25 changes: 20 additions & 5 deletions shell/platform/windows/flutter_windows_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <sstream>

#include "flutter/fml/logging.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/platform/win/wstring_conversion.h"
#include "flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h"
#include "flutter/shell/platform/common/path_utils.h"
Expand Down Expand Up @@ -224,8 +225,9 @@ bool FlutterWindowsEngine::Run(std::string_view 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::string executable_name = GetExecutableName();
std::vector<const char*> argv = {executable_name.c_str()};
std::vector<std::string> switches = project_->GetSwitches();
std::vector<const char*> argv = {"placeholder"};
std::transform(
switches.begin(), switches.end(), std::back_inserter(argv),
[](const std::string& arg) -> const char* { return arg.c_str(); });
Expand Down Expand Up @@ -532,10 +534,9 @@ void FlutterWindowsEngine::SendSystemLocales() {
// Convert the locale list to the locale pointer list that must be provided.
std::vector<const FlutterLocale*> flutter_locale_list;
flutter_locale_list.reserve(flutter_locales.size());
std::transform(
flutter_locales.begin(), flutter_locales.end(),
std::back_inserter(flutter_locale_list),
[](const auto& arg) -> const auto* { return &arg; });
std::transform(flutter_locales.begin(), flutter_locales.end(),
std::back_inserter(flutter_locale_list),
[](const auto& arg) -> const auto* { return &arg; });
embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
flutter_locale_list.size());
}
Expand Down Expand Up @@ -592,4 +593,18 @@ gfx::NativeViewAccessible FlutterWindowsEngine::GetNativeAccessibleFromId(
return node_delegate->GetNativeViewAccessible();
}

std::string FlutterWindowsEngine::GetExecutableName() const {
std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
if (result.first) {
const std::string& executable_path = result.second;
size_t last_separator = executable_path.find_last_of("/\\");
if (last_separator == std::string::npos ||
last_separator == executable_path.size() - 1) {
return executable_path;
}
return executable_path.substr(last_separator + 1);
}
return "Flutter";
}

} // namespace flutter
3 changes: 3 additions & 0 deletions shell/platform/windows/flutter_windows_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ class FlutterWindowsEngine {
root_isolate_create_callback_ = callback;
}

// Returns the executable name for this process or "Flutter" if unknown.
std::string GetExecutableName() const;

private:
// Allows swapping out embedder_api_ calls in tests.
friend class EngineModifier;
Expand Down
5 changes: 5 additions & 0 deletions shell/platform/windows/flutter_windows_engine_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -404,5 +404,10 @@ TEST(FlutterWindowsEngine, SetNextFrameCallback) {
EXPECT_TRUE(called);
}

TEST(FlutterWindowsEngine, GetExecutableName) {
std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
EXPECT_EQ(engine->GetExecutableName(), "flutter_windows_unittests.exe");
}

} // namespace testing
} // namespace flutter
24 changes: 24 additions & 0 deletions shell/platform/windows/flutter_windows_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,30 @@ TEST_F(WindowsTest, VerifyNativeFunctionWithParameters) {
EXPECT_TRUE(bool_value);
}

// Verify that Platform.executable returns the executable name.
TEST_F(WindowsTest, PlatformExecutable) {
auto& context = GetContext();
WindowsConfigBuilder builder(context);
builder.SetDartEntrypoint("readPlatformExecutable");

std::string executable_name;
fml::AutoResetWaitableEvent latch;
auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
auto handle = Dart_GetNativeArgument(args, 0);
ASSERT_FALSE(Dart_IsError(handle));
executable_name = tonic::DartConverter<std::string>::FromDart(handle);
latch.Signal();
});
context.AddNativeFunction("SignalStringValue", native_entry);

ViewControllerPtr controller{builder.Run()};
ASSERT_NE(controller, nullptr);

// Wait until signalStringValue has been called.
latch.Wait();
EXPECT_EQ(executable_name, "flutter_windows_unittests.exe");
}

// Verify that native functions that return values can be registered and
// resolved.
TEST_F(WindowsTest, VerifyNativeFunctionWithReturn) {
Expand Down

0 comments on commit 7fb8311

Please sign in to comment.