Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets/directory_asset_bundle.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class DirectoryAssetBundle {

bool GetAsBuffer(const std::string& asset_name, std::vector<uint8_t>* data);

private:
std::string GetPathForAsset(const std::string& asset_name);

private:
const std::string directory_;

FXL_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle);
Expand Down
41 changes: 36 additions & 5 deletions runtime/dart_init.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <utility>
#include <vector>

#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/assets/unzipper_provider.h"
#include "flutter/assets/zip_asset_store.h"
#include "flutter/common/settings.h"
Expand All @@ -34,6 +35,7 @@
#include "flutter/runtime/start_up.h"
#include "lib/fxl/arraysize.h"
#include "lib/fxl/build_config.h"
#include "lib/fxl/files/path.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/time/time_delta.h"
#include "lib/tonic/converter/dart_converter.h"
Expand Down Expand Up @@ -272,6 +274,16 @@ Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri,
#endif // FLUTTER_RUNTIME_MODE
}

static bool GetAssetAsBuffer(
const std::string& name,
std::vector<uint8_t>* data,
std::unique_ptr<DirectoryAssetBundle>& directory_asset_bundle,
fxl::RefPtr<ZipAssetStore>& asset_store) {
return (directory_asset_bundle &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we maybe split this up into multiple lines with early returns on failure conditions for readability?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could probably be split up, but there are no failure conditions as such here. The best we could do would be something like

if (directory_asset_bundle && 
    directory_asset_bundle->GetAsBuffer(name, data)) {
  return true;
}
return asset_store && asset_store->GetAsBuffer(name, data);

(I kind of prefer the current version, though, which is copied from engine. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I didn't realize this was copied directly from the engine. Our preferences don't match but I don't have a strong opinion about this. Lets keep it as it.

directory_asset_bundle->GetAsBuffer(name, data)) ||
(asset_store && asset_store->GetAsBuffer(name, data));
}

Dart_Isolate IsolateCreateCallback(const char* script_uri,
const char* main,
const char* package_root,
Expand Down Expand Up @@ -303,11 +315,30 @@ Dart_Isolate IsolateCreateCallback(const char* script_uri,
if (!running_from_source) {
// Attempt to copy the snapshot from the asset bundle.
const std::string& bundle_path = entry_path;
fxl::RefPtr<ZipAssetStore> zip_asset_store =
fxl::MakeRefCounted<ZipAssetStore>(
GetUnzipperProviderForPath(std::move(bundle_path)));
zip_asset_store->GetAsBuffer(kKernelAssetKey, &kernel_data);
zip_asset_store->GetAsBuffer(kSnapshotAssetKey, &snapshot_data);

struct stat stat_result = {};
if (::stat(bundle_path.c_str(), &stat_result) == 0) {
std::unique_ptr<DirectoryAssetBundle> directory_asset_bundle;
// TODO(zarah): Remove usage of zip_asset_store once app.flx is removed.
fxl::RefPtr<ZipAssetStore> zip_asset_store;
// bundle_path is either the path to app.flx or the flutter assets
// directory.
std::string flx_path = bundle_path;
if (S_ISDIR(stat_result.st_mode)) {
directory_asset_bundle =
std::make_unique<DirectoryAssetBundle>(bundle_path);
flx_path = files::GetDirectoryName(bundle_path) + "/app.flx";
}

if (access(flx_path.c_str(), R_OK) == 0) {
zip_asset_store = fxl::MakeRefCounted<ZipAssetStore>(
GetUnzipperProviderForPath(flx_path));
}
GetAssetAsBuffer(kKernelAssetKey, &kernel_data, directory_asset_bundle,
zip_asset_store);
GetAssetAsBuffer(kSnapshotAssetKey, &snapshot_data,
directory_asset_bundle, zip_asset_store);
}
}
}

Expand Down
23 changes: 16 additions & 7 deletions shell/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,17 @@ void Engine::Init(const std::string& bundle_path) {
#error Unknown OS
#endif

std::string flx_path = bundle_path;
struct stat stat_result = {};
if (::stat(flx_path.c_str(), &stat_result) == 0) {
if (S_ISDIR(stat_result.st_mode)) {
flx_path = files::GetDirectoryName(bundle_path) + "/app.flx";
}
}

blink::InitRuntime(vm_snapshot_data, vm_snapshot_instr,
default_isolate_snapshot_data,
default_isolate_snapshot_instr, bundle_path);
default_isolate_snapshot_instr, flx_path);
}

const std::string Engine::main_entrypoint_ = "main";
Expand Down Expand Up @@ -579,18 +587,18 @@ void Engine::ConfigureAssetBundle(const std::string& path) {
return;
}

std::string flx_path;
if (S_ISDIR(stat_result.st_mode)) {
directory_asset_bundle_ =
std::make_unique<blink::DirectoryAssetBundle>(path);
return;
flx_path = files::GetDirectoryName(path) + "/app.flx";
} else if (S_ISREG(stat_result.st_mode)) {
flx_path = path;
}

if (S_ISREG(stat_result.st_mode)) {
if (PathExists(flx_path)) {
asset_store_ = fxl::MakeRefCounted<blink::ZipAssetStore>(
blink::GetUnzipperProviderForPath(path));
directory_asset_bundle_ = std::make_unique<blink::DirectoryAssetBundle>(
files::GetDirectoryName(path));
return;
blink::GetUnzipperProviderForPath(flx_path));
}
}

Expand Down Expand Up @@ -689,6 +697,7 @@ void Engine::HandleAssetPlatformMessage(
const auto& data = message->data();
std::string asset_name(reinterpret_cast<const char*>(data.data()),
data.size());

std::vector<uint8_t> asset_data;
if (GetAssetAsBuffer(asset_name, &asset_data)) {
response->Complete(std::move(asset_data));
Expand Down
2 changes: 1 addition & 1 deletion shell/common/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class Engine : public blink::RuntimeDelegate {
std::string country_code_;
std::string user_settings_data_;
bool semantics_enabled_ = false;
// TODO(abarth): Unify these two behind a common interface.
// TODO(zarah): Remove usage of asset_store_ once app.flx is removed.
fxl::RefPtr<blink::ZipAssetStore> asset_store_;
std::unique_ptr<blink::DirectoryAssetBundle> directory_asset_bundle_;
// TODO(eseidel): This should move into an AnimatorStateMachine.
Expand Down
3 changes: 3 additions & 0 deletions shell/common/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ DEF_SWITCH(EnableTxt,
"enable-txt",
"Enable libtxt as the text shaping library instead of Blink.")
DEF_SWITCH(FLX, "flx", "Specify the FLX path.")
DEF_SWITCH(FlutterAssetsDir,
"flutter-assets-dir",
"Path to the Flutter assets directory.")
DEF_SWITCH(Help, "help", "Display this help text.")
DEF_SWITCH(LogTag, "log-tag", "Tag associated with log messages.")
DEF_SWITCH(MainDartFile, "dart-main", "The path to the main Dart file.")
Expand Down
10 changes: 8 additions & 2 deletions shell/platform/android/io/flutter/view/FlutterMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class FlutterMain {
private static final String AOT_ISOLATE_SNAPSHOT_INSTR_KEY = "isolate-snapshot-instr";
private static final String FLX_KEY = "flx";
private static final String SNAPSHOT_BLOB_KEY = "snapshot-blob";
private static final String FLUTTER_ASSETS_DIR_KEY = "flutter-assets-dir";

// XML Attribute keys supported in AndroidManifest.xml
public static final String PUBLIC_AOT_AOT_SHARED_LIBRARY_PATH =
Expand All @@ -54,6 +55,8 @@ public class FlutterMain {
FlutterMain.class.getName() + '.' + FLX_KEY;
public static final String PUBLIC_SNAPSHOT_BLOB_KEY =
FlutterMain.class.getName() + '.' + SNAPSHOT_BLOB_KEY;
public static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this was never wired up in initConfig(), so even if apps specify this key in their manifest, we're still looking in the default assets dir.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, thanks!

FlutterMain.class.getName() + '.' + FLUTTER_ASSETS_DIR_KEY;

// Resource names used for components of the precompiled snapshot.
private static final String DEFAULT_AOT_SHARED_LIBRARY_PATH= "app.so";
Expand All @@ -63,6 +66,7 @@ public class FlutterMain {
private static final String DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR = "isolate_snapshot_instr";
private static final String DEFAULT_FLX = "app.flx";
private static final String DEFAULT_SNAPSHOT_BLOB = "snapshot_blob.bin";
private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets";

private static final String MANIFEST = "flutter.yaml";

Expand All @@ -79,6 +83,7 @@ public class FlutterMain {
private static String sAotIsolateSnapshotInstr = DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR;
private static String sFlx = DEFAULT_FLX;
private static String sSnapshotBlob = DEFAULT_SNAPSHOT_BLOB;
private static String sFlutterAssetsDir = DEFAULT_FLUTTER_ASSETS_DIR;

private static boolean sInitialized = false;
private static ResourceExtractor sResourceExtractor;
Expand Down Expand Up @@ -238,7 +243,8 @@ private static void initResources(Context applicationContext) {
new ResourceCleaner(context).start();
sResourceExtractor = new ResourceExtractor(context)
.addResources(SKY_RESOURCES)
.addResource(sFlx);
.addResource(sFlx)
.addResource(sFlutterAssetsDir);
if (sIsPrecompiledAsSharedLibrary) {
sResourceExtractor
.addResource(sAotSharedLibraryPath);
Expand Down Expand Up @@ -290,7 +296,7 @@ public static boolean isRunningPrecompiledCode() {

public static String findAppBundlePath(Context applicationContext) {
String dataDirectory = PathUtils.getDataDirectory(applicationContext);
File appBundle = new File(dataDirectory, sFlx);
File appBundle = new File(dataDirectory, sFlutterAssetsDir);
return appBundle.exists() ? appBundle.getPath() : null;
}
}
67 changes: 40 additions & 27 deletions shell/platform/android/io/flutter/view/ResourceExtractor.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;

Expand Down Expand Up @@ -48,37 +50,40 @@ private void extractResources() {
try {
byte[] buffer = null;
final String[] assets = manager.list("");
for (String asset : assets) {
LinkedList<String> assetList = new LinkedList<>(Arrays.asList(assets));
while(!assetList.isEmpty()) {
String asset = assetList.pop();
if (!mResources.contains(asset))
continue;

if (manager.list(asset).length > 0) {
// The asset is a directory
for (String a: manager.list(asset)) {
assetList.add(asset + File.separator + a);
mResources.add(asset + File.separator + a);
}
continue;
}

final File output = new File(dataDir, asset);
if (output.exists())
continue;
InputStream is = null;
OutputStream os = null;
try {
is = manager.open(asset);
os = new FileOutputStream(output);
if (buffer == null) {
buffer = new byte[BUFFER_SIZE];
}

int count = 0;
while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) {
os.write(buffer, 0, count);
}
os.flush();
} finally {
try {
if (is != null) {
is.close();
}
} finally {
if (os != null) {
os.close();
}
}
if (output.getParentFile() != null) {
output.getParentFile().mkdirs();
}
try (InputStream is = manager.open(asset)) {
try (OutputStream os = new FileOutputStream(output)) {
if (buffer == null) {
buffer = new byte[BUFFER_SIZE];
}

int count = 0;
while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) {
os.write(buffer, 0, count);
}
os.flush();
}
}
}
} catch (IOException e) {
Log.w(TAG, "Exception unpacking resources: " + e.getMessage());
Expand Down Expand Up @@ -179,10 +184,18 @@ public boolean accept(File dir, String name) {

private void deleteFiles() {
final File dataDir = new File(PathUtils.getDataDirectory(mContext));
for (String resource : mResources) {
LinkedList<String> files = new LinkedList<>(mResources);
while (!files.isEmpty()) {
String resource = files.pop();
final File file = new File(dataDir, resource);
if (file.exists()) {
file.delete();
if (file.isFile()) {
file.delete();
} else {
for (String f : file.list()) {
files.add(resource + File.separator + f);
}
}
}
}
for (String timestamp : getExistingTimestamps(dataDir)) {
Expand Down
17 changes: 9 additions & 8 deletions shell/platform/darwin/common/platform_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,21 @@ bool AttemptLaunchFromCommandLineSwitches(Engine* engine) {

const auto& command_line = shell::Shell::Shared().GetCommandLine();

if (command_line.HasOption(FlagForSwitch(Switch::FLX)) ||
if (command_line.HasOption(FlagForSwitch(Switch::FlutterAssetsDir)) ||
command_line.HasOption(FlagForSwitch(Switch::MainDartFile)) ||
command_line.HasOption(FlagForSwitch(Switch::Packages))) {
// 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:@(FlagForSwitch(Switch::FLX).data())];
// The main dart file, Flutter assets directory 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:@(FlagForSwitch(Switch::FlutterAssetsDir).data())];
[defaults removeObjectForKey:@(FlagForSwitch(Switch::MainDartFile).data())];
[defaults removeObjectForKey:@(FlagForSwitch(Switch::Packages).data())];

[defaults synchronize];
}

std::string bundle_path = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::FLX));
std::string bundle_path = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::FlutterAssetsDir));
std::string main = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::MainDartFile));
std::string packages = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::Packages));

Expand All @@ -140,7 +140,8 @@ bool AttemptLaunchFromCommandLineSwitches(Engine* engine) {
// 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:@(bundle_path.c_str()) forKey:@(FlagForSwitch(Switch::FLX).data())];
[defaults setObject:@(bundle_path.c_str())
forKey:@(FlagForSwitch(Switch::FlutterAssetsDir).data())];
[defaults setObject:@(main.c_str()) forKey:@(FlagForSwitch(Switch::MainDartFile).data())];
[defaults setObject:@(packages.c_str()) forKey:@(FlagForSwitch(Switch::Packages).data())];

Expand Down
7 changes: 4 additions & 3 deletions shell/platform/darwin/desktop/platform_view_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@

void PlatformViewMac::SetupAndLoadDart() {
if (AttemptLaunchFromCommandLineSwitches(&engine())) {
// This attempts launching from an FLX bundle that does not contain a
// dart snapshot.
// This attempts launching from a Flutter assets directory that does not
// contain a dart snapshot.
return;
}

const auto& command_line = shell::Shell::Shared().GetCommandLine();

std::string bundle_path = command_line.GetOptionValueWithDefault(FlagForSwitch(Switch::FLX), "");
std::string bundle_path =
command_line.GetOptionValueWithDefault(FlagForSwitch(Switch::FlutterAssetsDir), "");
if (!bundle_path.empty()) {
blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), bundle_path ] {
if (engine)
Expand Down
7 changes: 7 additions & 0 deletions shell/platform/darwin/ios/framework/Headers/Flutter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
/**
BREAKING CHANGES:

December 11, 2017: Deprecated "initWithFLXArchive" and
"initWithFLXArchiveWithScriptSnapshot" and scheculed the same to be marked as
unavailable on January 15, 2018. Instead, "initWithFlutterAssets" and
"initWithFlutterAssetsWithScriptSnapshot" should be used. The reason for this
change is that the FLX archive will be deprecated and replaced with a flutter
assets directory containing the same files as the FLX did.

November 29, 2017: Added a BREAKING CHANGES section.
*/

Expand Down
19 changes: 16 additions & 3 deletions shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,22 @@ FLUTTER_EXPORT

- (instancetype)initWithFLXArchive:(NSURL*)archiveURL
dartMain:(NSURL*)dartMainURL
packages:(NSURL*)dartPackages NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)archiveURL NS_DESIGNATED_INITIALIZER;
packages:(NSURL*)dartPackages NS_DESIGNATED_INITIALIZER
FLUTTER_DEPRECATED(
"This initializer is no longer used since the FLX will be deprecated. "
"Instead, use [initWithFlutterAssets].");

- (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)archiveURL NS_DESIGNATED_INITIALIZER
FLUTTER_DEPRECATED(
"This initializer is no longer used since the FLX will be deprecated. "
"Instead, use [initWithFlutterAssetsWithScriptSnapshot].");

- (instancetype)initWithFlutterAssets:(NSURL*)flutterAssetsURL
dartMain:(NSURL*)dartMainURL
packages:(NSURL*)dartPackages NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssetsURL
NS_DESIGNATED_INITIALIZER;

- (instancetype)initFromDefaultSourceForConfiguration;

Expand Down
Loading