From f9281a1edf9622ccaffe125711d704d511e6bb33 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 29 Feb 2016 11:17:38 -0800 Subject: [PATCH] Allow specification of the FLX bundle (containing no Dart snapshot) outside the iOS/Mac application bundle. Allows launching of apps without having any Xcodebuild step in the simulator. --- sky/services/engine/sky_engine.mojom | 2 +- sky/shell/platform/ios/main_ios.mm | 8 +- sky/shell/platform/ios/sky_surface.mm | 42 +++++++++-- sky/shell/platform/mac/main_mac.mm | 2 +- sky/shell/platform/mac/platform_mac.h | 20 ++--- sky/shell/platform/mac/platform_mac.mm | 100 ++++++++++++++++++++++++- sky/shell/platform/mac/sky_window.mm | 34 ++++----- sky/shell/switches.cc | 1 + sky/shell/switches.h | 1 + sky/shell/testing/test_runner.cc | 8 +- sky/shell/ui/engine.cc | 62 +++++++-------- sky/shell/ui/engine.h | 7 +- 12 files changed, 207 insertions(+), 80 deletions(-) diff --git a/sky/services/engine/sky_engine.mojom b/sky/services/engine/sky_engine.mojom index 0becda212..00a769dd8 100644 --- a/sky/services/engine/sky_engine.mojom +++ b/sky/services/engine/sky_engine.mojom @@ -48,7 +48,7 @@ interface SkyEngine { PushRoute(string route); PopRoute(); - RunFromFile(string main, string package_root); + RunFromFile(string main, string package_root, string bundle); RunFromPrecompiledSnapshot(string path); RunFromBundle(string path); diff --git a/sky/shell/platform/ios/main_ios.mm b/sky/shell/platform/ios/main_ios.mm index eb8583654..ce49e9ee0 100644 --- a/sky/shell/platform/ios/main_ios.mm +++ b/sky/shell/platform/ios/main_ios.mm @@ -3,13 +3,13 @@ // found in the LICENSE file. #import -#import "sky/shell/platform/ios/sky_app_delegate.h" +#include "sky/shell/platform/ios/sky_app_delegate.h" #include "sky/shell/platform/mac/platform_mac.h" -int main(int argc, const char * argv[]) { - return PlatformMacMain(argc, argv, ^(){ - return UIApplicationMain(argc, (char **)argv, nil, +int main(int argc, const char* argv[]) { + return sky::shell::PlatformMacMain(argc, argv, ^() { + return UIApplicationMain(argc, (char**)argv, nil, NSStringFromClass([SkyAppDelegate class])); }); } diff --git a/sky/shell/platform/ios/sky_surface.mm b/sky/shell/platform/ios/sky_surface.mm index b017690ca..811652c3b 100644 --- a/sky/shell/platform/ios/sky_surface.mm +++ b/sky/shell/platform/ios/sky_surface.mm @@ -8,6 +8,7 @@ #import #import +#include "base/command_line.h" #include "base/logging.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" @@ -15,10 +16,12 @@ #include "sky/services/engine/input_event.mojom.h" #include "sky/services/pointer/pointer.mojom.h" #include "sky/shell/platform/ios/sky_dynamic_service_loader.h" +#include "sky/shell/platform/mac/platform_mac.h" #include "sky/shell/platform/mac/platform_service_provider.h" #include "sky/shell/platform/mac/platform_view_mac.h" #include "sky/shell/shell.h" #include "sky/shell/shell_view.h" +#include "sky/shell/switches.h" #include "sky/shell/ui_delegate.h" #include @@ -226,7 +229,6 @@ static std::string TracesBasePath() { // In case this runner is part of the precompilation SDK, the FLX bundle is // present in the application bundle instead of the runner bundle. Attempt // to resolve the path there first. - // TODO: Allow specification of the application bundle identifier NSBundle* applicationBundle = [NSBundle bundleWithIdentifier:@"io.flutter.application.FlutterApplication"]; NSString* path = [applicationBundle pathForResource:@"app" ofType:@"flx"]; @@ -251,18 +253,44 @@ static std::string TracesBasePath() { services->services_provided_by_embedder = service_provider.Pass(); _engine->SetServices(services.Pass()); - mojo::String bundle_path([self flxBundlePath]); - - CHECK(bundle_path.size() != 0) - << "There must be a valid FLX bundle to run the application"; +#if TARGET_IPHONE_SIMULATOR + [self runFromDartSource]; +#else + [self runFromPrecompiledSource]; +#endif +} #if TARGET_IPHONE_SIMULATOR - _engine->RunFromBundle(bundle_path); + +- (void)runFromDartSource { + if (sky::shell::AttemptLaunchFromCommandLineSwitches(_engine)) { + return; + } + + UIAlertView* alert = [[UIAlertView alloc] + initWithTitle:@"Error" + message:@"Could not resolve one or all of either the main dart " + @"file path, the FLX bundle path or the package root " + @"on the host. Use the tooling to relaunch the " + @"application." + delegate:self + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alert show]; + [alert release]; +} + #else + +- (void)runFromPrecompiledSource { + mojo::String bundle_path([self flxBundlePath]); + CHECK(bundle_path.size() != 0) + << "There must be a valid FLX bundle to run the application"; _engine->RunFromPrecompiledSnapshot(bundle_path); -#endif } +#endif // TARGET_IPHONE_SIMULATOR + #pragma mark - UIResponder overrides for raw touches - (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase { diff --git a/sky/shell/platform/mac/main_mac.mm b/sky/shell/platform/mac/main_mac.mm index 523f0af5d..2846f926c 100644 --- a/sky/shell/platform/mac/main_mac.mm +++ b/sky/shell/platform/mac/main_mac.mm @@ -32,7 +32,7 @@ void AttachMessageLoopToMainRunLoop(void) { int main(int argc, const char* argv[]) { [SkyApplication sharedApplication]; - return PlatformMacMain(argc, argv, ^() { + return sky::shell::PlatformMacMain(argc, argv, ^() { base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(sky::shell::switches::kHelp)) { sky::shell::switches::PrintUsage("SkyShell"); diff --git a/sky/shell/platform/mac/platform_mac.h b/sky/shell/platform/mac/platform_mac.h index 6e0cf4b34..c74b46597 100644 --- a/sky/shell/platform/mac/platform_mac.h +++ b/sky/shell/platform/mac/platform_mac.h @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SKY_SHELL_MAC_PLATFORM_MAC_H_ -#define SKY_SHELL_MAC_PLATFORM_MAC_H_ +#ifndef SKY_SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ +#define SKY_SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ -#ifdef __cplusplus -extern "C" { -#endif +#include "sky/services/engine/sky_engine.mojom.h" + +namespace sky { +namespace shell { typedef int (^PlatformMacMainCallback)(void); @@ -15,8 +16,9 @@ int PlatformMacMain(int argc, const char* argv[], PlatformMacMainCallback callback); -#ifdef __cplusplus -} -#endif +bool AttemptLaunchFromCommandLineSwitches(sky::SkyEnginePtr& engine); + +} // namespace shell +} // namespace sky -#endif // SKY_SHELL_MAC_PLATFORM_MAC_H_ +#endif // SKY_SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ diff --git a/sky/shell/platform/mac/platform_mac.mm b/sky/shell/platform/mac/platform_mac.mm index 845a371be..5e028af1f 100644 --- a/sky/shell/platform/mac/platform_mac.mm +++ b/sky/shell/platform/mac/platform_mac.mm @@ -4,6 +4,8 @@ #include "sky/shell/platform/mac/platform_mac.h" +#include + #include #include "base/at_exit.h" #include "base/command_line.h" @@ -21,6 +23,9 @@ #include "ui/gl/gl_surface.h" #include "base/trace_event/trace_event.h" +namespace sky { +namespace shell { + static void InitializeLogging() { logging::LoggingSettings settings; settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; @@ -72,7 +77,8 @@ int PlatformMacMain(int argc, // marker that can be used as a reference for startup. TRACE_EVENT_INSTANT0("flutter", "main", TRACE_EVENT_SCOPE_PROCESS); - std::unique_ptr message_loop(new base::MessageLoopForUI()); + std::unique_ptr message_loop( + new base::MessageLoopForUI()); #if TARGET_OS_IPHONE // One cannot start the message loop on the platform main thread. Instead, @@ -96,3 +102,95 @@ int PlatformMacMain(int argc, return result; } + +static bool FlagsValidForCommandLineLaunch(const std::string& dart_main, + const std::string& package_root, + const std::string& bundle) { + if (dart_main.size() == 0 || package_root.size() == 0 || bundle.size() == 0) { + return false; + } + + // Ensure that the paths exists. This catches cases where the user has + // successfully launched the application from the tooling but has since moved + // the source files on disk and is launching again directly. + + NSFileManager* manager = [NSFileManager defaultManager]; + + if (![manager fileExistsAtPath:@(dart_main.c_str())]) { + return false; + } + + if (![manager fileExistsAtPath:@(package_root.c_str())]) { + return false; + } + + if (![manager fileExistsAtPath:@(bundle.c_str())]) { + return false; + } + + return true; +} + +static std::string ResolveCommandLineLaunchFlag(const char* name) { + auto command_line = *base::CommandLine::ForCurrentProcess(); + + if (command_line.HasSwitch(name)) { + return command_line.GetSwitchValueASCII(name); + } + + const char* saved_default = + [[NSUserDefaults standardUserDefaults] stringForKey:@(name)].UTF8String; + + if (saved_default != NULL) { + return saved_default; + } + + return ""; +} + +bool AttemptLaunchFromCommandLineSwitches(sky::SkyEnginePtr& engine) { + base::mac::ScopedNSAutoreleasePool pool; + + using namespace sky::shell::switches; + + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + + auto command_line = *base::CommandLine::ForCurrentProcess(); + + if (command_line.HasSwitch(kMainDartFile) || + command_line.HasSwitch(kPackageRoot) || command_line.HasSwitch(kFLX)) { + // The main dart file, flx bundle and the package root must be specified in + // one go. We dont want to end up in a situation where we take one value + // from the command line and the others from user defaults. In case, any + // new flags are specified, forget about all the old ones. + [defaults removeObjectForKey:@(kMainDartFile)]; + [defaults removeObjectForKey:@(kPackageRoot)]; + [defaults removeObjectForKey:@(kFLX)]; + + [defaults synchronize]; + } + + std::string dart_main = ResolveCommandLineLaunchFlag(kMainDartFile); + std::string package_root = ResolveCommandLineLaunchFlag(kPackageRoot); + std::string bundle = ResolveCommandLineLaunchFlag(kFLX); + + if (!FlagsValidForCommandLineLaunch(dart_main, package_root, bundle)) { + return false; + } + + // Save the newly resolved dart main file and the package root to user + // defaults so that the next time the user launches the application in the + // simulator without the tooling, the application boots up. + [defaults setObject:@(dart_main.c_str()) forKey:@(kMainDartFile)]; + [defaults setObject:@(package_root.c_str()) forKey:@(kPackageRoot)]; + [defaults setObject:@(bundle.c_str()) forKey:@(kFLX)]; + + [defaults synchronize]; + + // Finally launch with the newly resolved arguments. + engine->RunFromFile(dart_main, package_root, bundle); + return true; +} + +} // namespace shell +} // namespace sky diff --git a/sky/shell/platform/mac/sky_window.mm b/sky/shell/platform/mac/sky_window.mm index ab0c7e644..bcbbba589 100644 --- a/sky/shell/platform/mac/sky_window.mm +++ b/sky/shell/platform/mac/sky_window.mm @@ -8,6 +8,7 @@ #include "mojo/public/cpp/bindings/interface_request.h" #include "sky/services/engine/input_event.mojom.h" #include "sky/services/pointer/pointer.mojom.h" +#include "sky/shell/platform/mac/platform_mac.h" #include "sky/shell/platform/mac/platform_view_mac.h" #include "sky/shell/platform/mac/platform_service_provider.h" #include "sky/shell/shell_view.h" @@ -16,8 +17,7 @@ #include "sky/shell/ui_delegate.h" static void DynamicServiceResolve(const mojo::String& service_name, - mojo::ScopedMessagePipeHandle handle) { -} + mojo::ScopedMessagePipeHandle handle) {} @interface SkyWindow () @@ -26,7 +26,8 @@ static void DynamicServiceResolve(const mojo::String& service_name, @end -static inline pointer::PointerType EventTypeFromNSEventPhase(NSEventPhase phase) { +static inline pointer::PointerType EventTypeFromNSEventPhase( + NSEventPhase phase) { switch (phase) { case NSEventPhaseNone: return pointer::PointerType::CANCEL; @@ -72,14 +73,8 @@ static inline pointer::PointerType EventTypeFromNSEventPhase(NSEventPhase phase) self.platformView->SurfaceCreated(widget); } -- (NSString*)skyInitialBundleURL { - return [[NSBundle mainBundle] pathForResource:@"app" ofType:@"flx"]; -} - // TODO(eseidel): This does not belong in sky_window! // Probably belongs in NSApplicationDelegate didFinishLaunching. -// We also want a separate setup for normal apps vs SkyShell -// normal apps only use a flx vs. SkyShell which always pulls from network. - (void)setupAndLoadDart { self.platformView->ConnectToEngine(mojo::GetProxy(&_sky_engine)); @@ -90,9 +85,16 @@ static inline pointer::PointerType EventTypeFromNSEventPhase(NSEventPhase phase) services->services_provided_by_embedder = service_provider.Pass(); _sky_engine->SetServices(services.Pass()); + if (sky::shell::AttemptLaunchFromCommandLineSwitches(_sky_engine)) { + // This attempts launching from an FLX bundle that does not contain a + // dart snapshot. + return; + } + base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - std::string flx = command_line.GetSwitchValueASCII(sky::shell::switches::kFLX); + std::string flx = + command_line.GetSwitchValueASCII(sky::shell::switches::kFLX); if (!flx.empty()) { _sky_engine->RunFromBundle(flx); return; @@ -100,15 +102,9 @@ static inline pointer::PointerType EventTypeFromNSEventPhase(NSEventPhase phase) auto args = command_line.GetArgs(); if (args.size() > 0) { - _sky_engine->RunFromFile(args[0], - command_line.GetSwitchValueASCII(sky::shell::switches::kPackageRoot)); - return; - } - - NSString *endpoint = self.skyInitialBundleURL; - if (endpoint.length > 0) { - mojo::String string(endpoint.UTF8String); - _sky_engine->RunFromBundle(string); + auto package_root = + command_line.GetSwitchValueASCII(sky::shell::switches::kPackageRoot); + _sky_engine->RunFromFile(args[0], package_root, ""); return; } } diff --git a/sky/shell/switches.cc b/sky/shell/switches.cc index cda070648..f5daafdc4 100644 --- a/sky/shell/switches.cc +++ b/sky/shell/switches.cc @@ -14,6 +14,7 @@ const char kEnableCheckedMode[] = "enable-checked-mode"; const char kFLX[] = "flx"; const char kHelp[] = "help"; const char kNonInteractive[] = "non-interactive"; +const char kMainDartFile[] = "dart-main"; const char kPackageRoot[] = "package-root"; const char kStartPaused[] = "start-paused"; const char kTraceStartup[] = "trace-startup"; diff --git a/sky/shell/switches.h b/sky/shell/switches.h index 999525137..6c08236ea 100644 --- a/sky/shell/switches.h +++ b/sky/shell/switches.h @@ -15,6 +15,7 @@ extern const char kEnableCheckedMode[]; extern const char kFLX[]; extern const char kHelp[]; extern const char kNonInteractive[]; +extern const char kMainDartFile[]; extern const char kPackageRoot[]; extern const char kStartPaused[]; extern const char kTraceStartup[]; diff --git a/sky/shell/testing/test_runner.cc b/sky/shell/testing/test_runner.cc index eeaac0a32..767421e84 100644 --- a/sky/shell/testing/test_runner.cc +++ b/sky/shell/testing/test_runner.cc @@ -22,8 +22,7 @@ static TestRunner* g_test_runner = nullptr; } // namespace TestRunner::TestRunner() - : shell_view_(new ShellView(Shell::Shared())), - weak_ptr_factory_(this) { + : shell_view_(new ShellView(Shell::Shared())), weak_ptr_factory_(this) { CHECK(!g_test_runner) << "Only create one TestRunner."; shell_view_->view()->ConnectToEngine(GetProxy(&sky_engine_)); @@ -33,8 +32,7 @@ TestRunner::TestRunner() sky_engine_->OnViewportMetricsChanged(metrics.Pass()); } -TestRunner::~TestRunner() { -} +TestRunner::~TestRunner() {} TestRunner& TestRunner::Shared() { if (!g_test_runner) @@ -43,7 +41,7 @@ TestRunner& TestRunner::Shared() { } void TestRunner::Run(const TestDescriptor& test) { - sky_engine_->RunFromFile(test.path, test.package_root); + sky_engine_->RunFromFile(test.path, test.package_root, ""); } } // namespace shell diff --git a/sky/shell/ui/engine.cc b/sky/shell/ui/engine.cc index 3e17ce21b..f9d1d5af1 100644 --- a/sky/shell/ui/engine.cc +++ b/sky/shell/ui/engine.cc @@ -37,11 +37,9 @@ PlatformImpl* g_platform_impl = nullptr; using mojo::asset_bundle::ZipAssetBundle; using mojo::asset_bundle::ZipAssetService; -Engine::Config::Config() { -} +Engine::Config::Config() {} -Engine::Config::~Config() { -} +Engine::Config::~Config() {} Engine::Engine(const Config& config, rasterizer::RasterizerPtr rasterizer) : config_(config), @@ -49,11 +47,9 @@ Engine::Engine(const Config& config, rasterizer::RasterizerPtr rasterizer) binding_(this), activity_running_(false), have_surface_(false), - weak_factory_(this) { -} + weak_factory_(this) {} -Engine::~Engine() { -} +Engine::~Engine() {} base::WeakPtr Engine::GetWeakPtr() { return weak_factory_.GetWeakPtr(); @@ -140,7 +136,7 @@ void Engine::OnViewportMetricsChanged(ViewportMetricsPtr metrics) { } void Engine::OnLocaleChanged(const mojo::String& language_code, - const mojo::String& country_code) { + const mojo::String& country_code) { language_code_ = language_code; country_code_ = country_code; if (sky_view_) @@ -185,13 +181,16 @@ void Engine::RunFromSnapshotStream( sky_view_->PushRoute(initial_route_); } +void Engine::ConfigureZipAssetBundle(const mojo::String& path) { + zip_asset_bundle_ = new ZipAssetBundle(base::FilePath(std::string{path}), + base::WorkerPool::GetTaskRunner(true)); + ZipAssetService::Create(mojo::GetProxy(&root_bundle_), zip_asset_bundle_); +} + void Engine::RunFromPrecompiledSnapshot(const mojo::String& bundle_path) { TRACE_EVENT0("flutter", "Engine::RunFromPrecompiledSnapshot"); - std::string path_str = bundle_path; - zip_asset_bundle_ = new ZipAssetBundle( - base::FilePath(path_str), base::WorkerPool::GetTaskRunner(true)); - ZipAssetService::Create(mojo::GetProxy(&root_bundle_), zip_asset_bundle_); + ConfigureZipAssetBundle(bundle_path); sky_view_ = blink::SkyView::Create(this); sky_view_->CreateView("http://localhost"); @@ -203,8 +202,13 @@ void Engine::RunFromPrecompiledSnapshot(const mojo::String& bundle_path) { } void Engine::RunFromFile(const mojo::String& main, - const mojo::String& package_root) { + const mojo::String& package_root, + const mojo::String& bundle) { TRACE_EVENT0("flutter", "Engine::RunFromFile"); + if (bundle.size() != 0) { + // The specification of an FLX bundle is optional. + ConfigureZipAssetBundle(bundle); + } std::string package_root_str = package_root; dart_library_provider_.reset( new DartLibraryProviderFiles(base::FilePath(package_root_str))); @@ -213,32 +217,29 @@ void Engine::RunFromFile(const mojo::String& main, void Engine::RunFromBundle(const mojo::String& path) { TRACE_EVENT0("flutter", "Engine::RunFromBundle"); - std::string path_str = path; - zip_asset_bundle_ = new ZipAssetBundle( - base::FilePath(path_str), base::WorkerPool::GetTaskRunner(true)); - ZipAssetService::Create(mojo::GetProxy(&root_bundle_), zip_asset_bundle_); - root_bundle_->GetAsStream(blink::kSnapshotAssetKey, - base::Bind(&Engine::RunFromSnapshotStream, - weak_factory_.GetWeakPtr(), path_str)); + ConfigureZipAssetBundle(path); + + root_bundle_->GetAsStream( + blink::kSnapshotAssetKey, + base::Bind(&Engine::RunFromSnapshotStream, weak_factory_.GetWeakPtr(), + std::string{path})); } void Engine::RunFromBundleAndSnapshot(const mojo::String& bundle_path, const mojo::String& snapshot_path) { TRACE_EVENT0("flutter", "Engine::RunFromBundleAndSnapshot"); - std::string bundle_path_str = bundle_path; - zip_asset_bundle_ = new ZipAssetBundle( - base::FilePath(bundle_path_str), base::WorkerPool::GetTaskRunner(true)); - ZipAssetService::Create(mojo::GetProxy(&root_bundle_), zip_asset_bundle_); + + ConfigureZipAssetBundle(bundle_path); std::string snapshot_path_str = snapshot_path; zip_asset_bundle_->AddOverlayFile(blink::kSnapshotAssetKey, base::FilePath(snapshot_path_str)); - root_bundle_->GetAsStream(blink::kSnapshotAssetKey, - base::Bind(&Engine::RunFromSnapshotStream, - weak_factory_.GetWeakPtr(), - bundle_path_str)); + root_bundle_->GetAsStream( + blink::kSnapshotAssetKey, + base::Bind(&Engine::RunFromSnapshotStream, weak_factory_.GetWeakPtr(), + std::string{bundle_path})); } void Engine::PushRoute(const mojo::String& route) { @@ -295,8 +296,7 @@ void Engine::FlushRealTimeEvents() { animator_->FlushRealTimeEvents(); } -void Engine::Render(std::unique_ptr layer_tree) { -} +void Engine::Render(std::unique_ptr layer_tree) {} } // namespace shell } // namespace sky diff --git a/sky/shell/ui/engine.h b/sky/shell/ui/engine.h index 202e2d058..7a7919fdc 100644 --- a/sky/shell/ui/engine.h +++ b/sky/shell/ui/engine.h @@ -64,11 +64,12 @@ class Engine : public UIDelegate, void SetServices(ServicesDataPtr services) override; void OnViewportMetricsChanged(ViewportMetricsPtr metrics) override; void OnLocaleChanged(const mojo::String& language_code, - const mojo::String& country_code) override; + const mojo::String& country_code) override; void OnPointerPacket(pointer::PointerPacketPtr packet) override; void RunFromFile(const mojo::String& main, - const mojo::String& package_root) override; + const mojo::String& package_root, + const mojo::String& bundle) override; void RunFromPrecompiledSnapshot(const mojo::String& bundle_path) override; void RunFromBundle(const mojo::String& path) override; void RunFromBundleAndSnapshot(const mojo::String& bundle_path, @@ -92,6 +93,8 @@ class Engine : public UIDelegate, void StopAnimator(); void StartAnimatorIfPossible(); + void ConfigureZipAssetBundle(const mojo::String& path); + Config config_; std::unique_ptr animator_; -- GitLab