Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b6644733
Commit
b6644733
authored
Jul 22, 2016
by
John McCutchan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support for synchronizing assets onto a DevFS
parent
1fe118fe
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
560 additions
and
426 deletions
+560
-426
asset.dart
packages/flutter_tools/lib/src/asset.dart
+418
-0
devfs.dart
packages/flutter_tools/lib/src/devfs.dart
+82
-6
flx.dart
packages/flutter_tools/lib/src/flx.dart
+15
-372
zip.dart
packages/flutter_tools/lib/src/zip.dart
+15
-44
devfs_test.dart
packages/flutter_tools/test/devfs_test.dart
+23
-4
mocks.dart
packages/flutter_tools/test/src/mocks.dart
+7
-0
No files found.
packages/flutter_tools/lib/src/asset.dart
0 → 100644
View file @
b6644733
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'package:json_schema/json_schema.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:yaml/yaml.dart'
;
import
'cache.dart'
;
import
'dart/package_map.dart'
;
import
'globals.dart'
;
/// An entry in an asset bundle.
class
AssetBundleEntry
{
/// An entry backed by a File.
AssetBundleEntry
.
fromFile
(
this
.
archivePath
,
this
.
file
)
:
_contents
=
null
;
/// An entry backed by a String.
AssetBundleEntry
.
fromString
(
this
.
archivePath
,
this
.
_contents
)
:
file
=
null
;
/// The path within the bundle.
final
String
archivePath
;
/// The payload.
List
<
int
>
contentsAsBytes
()
{
if
(
_contents
!=
null
)
{
return
UTF8
.
encode
(
_contents
);
}
else
{
return
file
.
readAsBytesSync
();
}
}
bool
get
isStringEntry
=>
_contents
!=
null
;
final
File
file
;
final
String
_contents
;
}
/// A bundle of assets.
class
AssetBundle
{
final
Set
<
AssetBundleEntry
>
entries
=
new
Set
<
AssetBundleEntry
>();
static
const
String
defaultManifestPath
=
'flutter.yaml'
;
static
const
String
defaultWorkingDirPath
=
'build/flx'
;
static
const
String
_kFontSetMaterial
=
'material'
;
static
const
String
_kFontSetRoboto
=
'roboto'
;
Future
<
int
>
build
({
String
manifestPath:
defaultManifestPath
,
String
workingDirPath:
defaultWorkingDirPath
,
bool
includeRobotoFonts:
true
})
async
{
Object
manifest
=
_loadFlutterYamlManifest
(
manifestPath
);
if
(
manifest
!=
null
)
{
int
result
=
await
_validateFlutterYamlManifest
(
manifest
);
if
(
result
!=
0
)
return
result
;
}
Map
<
String
,
dynamic
>
manifestDescriptor
=
manifest
;
assert
(
manifestDescriptor
!=
null
);
String
assetBasePath
=
path
.
dirname
(
path
.
absolute
(
manifestPath
));
final
PackageMap
packageMap
=
new
PackageMap
(
path
.
join
(
assetBasePath
,
'.packages'
));
Map
<
_Asset
,
List
<
_Asset
>>
assetVariants
=
_parseAssets
(
packageMap
,
manifestDescriptor
,
assetBasePath
,
excludeDirs:
<
String
>[
workingDirPath
,
path
.
join
(
assetBasePath
,
'build'
)]
);
if
(
assetVariants
==
null
)
return
1
;
final
bool
usesMaterialDesign
=
(
manifestDescriptor
!=
null
)
&&
manifestDescriptor
[
'uses-material-design'
];
for
(
_Asset
asset
in
assetVariants
.
keys
)
{
AssetBundleEntry
assetEntry
=
_createAssetEntry
(
asset
);
if
(
assetEntry
==
null
)
return
1
;
entries
.
add
(
assetEntry
);
for
(
_Asset
variant
in
assetVariants
[
asset
])
{
AssetBundleEntry
variantEntry
=
_createAssetEntry
(
variant
);
if
(
variantEntry
==
null
)
return
1
;
entries
.
add
(
variantEntry
);
}
}
List
<
_Asset
>
materialAssets
=
<
_Asset
>[];
if
(
usesMaterialDesign
)
{
materialAssets
.
addAll
(
_getMaterialAssets
(
_kFontSetMaterial
));
if
(
includeRobotoFonts
)
materialAssets
.
addAll
(
_getMaterialAssets
(
_kFontSetRoboto
));
}
for
(
_Asset
asset
in
materialAssets
)
{
AssetBundleEntry
assetEntry
=
_createAssetEntry
(
asset
);
if
(
assetEntry
==
null
)
return
1
;
entries
.
add
(
assetEntry
);
}
entries
.
add
(
_createAssetManifest
(
assetVariants
));
AssetBundleEntry
fontManifest
=
_createFontManifest
(
manifestDescriptor
,
usesMaterialDesign
,
includeRobotoFonts
);
if
(
fontManifest
!=
null
)
entries
.
add
(
fontManifest
);
// TODO(ianh): Only do the following line if we've changed packages
entries
.
add
(
await
_obtainLicenses
(
packageMap
,
assetBasePath
));
return
0
;
}
void
dump
()
{
print
(
'Dumping AssetBundle:'
);
for
(
AssetBundleEntry
entry
in
entries
)
{
print
(
entry
.
archivePath
);
}
}
}
class
_Asset
{
_Asset
({
this
.
base
,
String
assetEntry
,
this
.
relativePath
,
this
.
source
})
{
this
.
_assetEntry
=
assetEntry
;
}
String
_assetEntry
;
final
String
base
;
/// The entry to list in the generated asset manifest.
String
get
assetEntry
=>
_assetEntry
??
relativePath
;
/// Where the resource is on disk relative to [base].
final
String
relativePath
;
final
String
source
;
File
get
assetFile
{
return
new
File
(
source
!=
null
?
'
$base
/
$source
'
:
'
$base
/
$relativePath
'
);
}
bool
get
assetFileExists
=>
assetFile
.
existsSync
();
/// The delta between what the assetEntry is and the relativePath (e.g.,
/// packages/flutter_gallery).
String
get
symbolicPrefix
{
if
(
_assetEntry
==
null
||
_assetEntry
==
relativePath
)
return
null
;
int
index
=
_assetEntry
.
indexOf
(
relativePath
);
return
index
==
-
1
?
null
:
_assetEntry
.
substring
(
0
,
index
);
}
@override
String
toString
()
=>
'asset:
$assetEntry
'
;
}
Map
<
String
,
dynamic
>
_readMaterialFontsManifest
()
{
String
fontsPath
=
path
.
join
(
path
.
absolute
(
Cache
.
flutterRoot
),
'packages'
,
'flutter_tools'
,
'schema'
,
'material_fonts.yaml'
);
return
loadYaml
(
new
File
(
fontsPath
).
readAsStringSync
());
}
final
Map
<
String
,
dynamic
>
_materialFontsManifest
=
_readMaterialFontsManifest
();
List
<
Map
<
String
,
dynamic
>>
_getMaterialFonts
(
String
fontSet
)
{
return
_materialFontsManifest
[
fontSet
];
}
List
<
_Asset
>
_getMaterialAssets
(
String
fontSet
)
{
List
<
_Asset
>
result
=
<
_Asset
>[];
for
(
Map
<
String
,
dynamic
>
family
in
_getMaterialFonts
(
fontSet
))
{
for
(
Map
<
String
,
dynamic
>
font
in
family
[
'fonts'
])
{
String
assetKey
=
font
[
'asset'
];
result
.
add
(
new
_Asset
(
base:
'
${Cache.flutterRoot}
/bin/cache/artifacts/material_fonts'
,
source
:
path
.
basename
(
assetKey
),
relativePath:
assetKey
));
}
}
return
result
;
}
final
String
_licenseSeparator
=
'
\n
'
+
(
'-'
*
80
)
+
'
\n
'
;
/// Returns a AssetBundleEntry representing the license file.
Future
<
AssetBundleEntry
>
_obtainLicenses
(
PackageMap
packageMap
,
String
assetBase
)
async
{
// Read the LICENSE file from each package in the .packages file,
// splitting each one into each component license (so that we can
// de-dupe if possible).
// For the sky_engine package we assume each license starts with
// package names. For the other packages we assume that each
// license is raw.
final
Map
<
String
,
Set
<
String
>>
packageLicenses
=
<
String
,
Set
<
String
>>{};
for
(
String
packageName
in
packageMap
.
map
.
keys
)
{
final
Uri
package
=
packageMap
.
map
[
packageName
];
if
(
package
!=
null
&&
package
.
scheme
==
'file'
)
{
final
File
file
=
new
File
.
fromUri
(
package
.
resolve
(
'../LICENSE'
));
if
(
file
.
existsSync
())
{
final
List
<
String
>
rawLicenses
=
(
await
file
.
readAsString
()).
split
(
_licenseSeparator
);
for
(
String
rawLicense
in
rawLicenses
)
{
String
licenseText
;
List
<
String
>
packageNames
;
if
(
packageName
==
'sky_engine'
)
{
final
int
split
=
rawLicense
.
indexOf
(
'
\n\n
'
);
if
(
split
>=
0
)
{
packageNames
=
rawLicense
.
substring
(
0
,
split
).
split
(
'
\n
'
);
licenseText
=
rawLicense
.
substring
(
split
+
2
);
}
}
if
(
licenseText
==
null
)
{
licenseText
=
rawLicense
;
packageNames
=
<
String
>[
packageName
];
}
packageLicenses
.
putIfAbsent
(
rawLicense
,
()
=>
new
Set
<
String
>())
..
addAll
(
packageNames
);
}
}
}
}
final
List
<
String
>
combinedLicensesList
=
packageLicenses
.
keys
.
map
(
(
String
license
)
{
List
<
String
>
packageNames
=
packageLicenses
[
license
].
toList
()
..
sort
();
return
packageNames
.
join
(
'
\n
'
)
+
'
\n\n
'
+
license
;
}
).
toList
();
combinedLicensesList
.
sort
();
final
String
combinedLicenses
=
combinedLicensesList
.
join
(
_licenseSeparator
);
return
new
AssetBundleEntry
.
fromString
(
'LICENSE'
,
combinedLicenses
);
}
/// Create a [AssetBundleEntry] from the given [_Asset]; the asset must exist.
AssetBundleEntry
_createAssetEntry
(
_Asset
asset
)
{
assert
(
asset
.
assetFileExists
);
return
new
AssetBundleEntry
.
fromFile
(
asset
.
assetEntry
,
asset
.
assetFile
);
}
AssetBundleEntry
_createAssetManifest
(
Map
<
_Asset
,
List
<
_Asset
>>
assetVariants
)
{
Map
<
String
,
List
<
String
>>
json
=
<
String
,
List
<
String
>>{};
for
(
_Asset
main
in
assetVariants
.
keys
)
{
List
<
String
>
variants
=
<
String
>[];
for
(
_Asset
variant
in
assetVariants
[
main
])
variants
.
add
(
variant
.
relativePath
);
json
[
main
.
relativePath
]
=
variants
;
}
return
new
AssetBundleEntry
.
fromString
(
'AssetManifest.json'
,
JSON
.
encode
(
json
));
}
AssetBundleEntry
_createFontManifest
(
Map
<
String
,
dynamic
>
manifestDescriptor
,
bool
usesMaterialDesign
,
bool
includeRobotoFonts
)
{
List
<
Map
<
String
,
dynamic
>>
fonts
=
<
Map
<
String
,
dynamic
>>[];
if
(
usesMaterialDesign
)
{
fonts
.
addAll
(
_getMaterialFonts
(
AssetBundle
.
_kFontSetMaterial
));
if
(
includeRobotoFonts
)
fonts
.
addAll
(
_getMaterialFonts
(
AssetBundle
.
_kFontSetRoboto
));
}
if
(
manifestDescriptor
!=
null
&&
manifestDescriptor
.
containsKey
(
'fonts'
))
fonts
.
addAll
(
manifestDescriptor
[
'fonts'
]);
if
(
fonts
.
isEmpty
)
return
null
;
return
new
AssetBundleEntry
.
fromString
(
'FontManifest.json'
,
JSON
.
encode
(
fonts
));
}
/// Given an assetBase location and a flutter.yaml manifest, return a map of
/// assets to asset variants.
///
/// Returns `null` on missing assets.
Map
<
_Asset
,
List
<
_Asset
>>
_parseAssets
(
PackageMap
packageMap
,
Map
<
String
,
dynamic
>
manifestDescriptor
,
String
assetBase
,
{
List
<
String
>
excludeDirs:
const
<
String
>[]
})
{
Map
<
_Asset
,
List
<
_Asset
>>
result
=
<
_Asset
,
List
<
_Asset
>>{};
if
(
manifestDescriptor
==
null
)
return
result
;
excludeDirs
=
excludeDirs
.
map
(
(
String
exclude
)
=>
path
.
absolute
(
exclude
)
+
Platform
.
pathSeparator
).
toList
();
if
(
manifestDescriptor
.
containsKey
(
'assets'
))
{
for
(
String
asset
in
manifestDescriptor
[
'assets'
])
{
_Asset
baseAsset
=
_resolveAsset
(
packageMap
,
assetBase
,
asset
);
if
(!
baseAsset
.
assetFileExists
)
{
printError
(
'Error: unable to locate asset entry in flutter.yaml: "
$asset
".'
);
return
null
;
}
List
<
_Asset
>
variants
=
<
_Asset
>[];
result
[
baseAsset
]
=
variants
;
// Find asset variants
String
assetPath
=
baseAsset
.
assetFile
.
path
;
String
assetFilename
=
path
.
basename
(
assetPath
);
Directory
assetDir
=
new
Directory
(
path
.
dirname
(
assetPath
));
List
<
FileSystemEntity
>
files
=
assetDir
.
listSync
(
recursive:
true
);
for
(
FileSystemEntity
entity
in
files
)
{
if
(!
FileSystemEntity
.
isFileSync
(
entity
.
path
))
continue
;
// Exclude any files in the given directories.
if
(
excludeDirs
.
any
((
String
exclude
)
=>
entity
.
path
.
startsWith
(
exclude
)))
continue
;
if
(
path
.
basename
(
entity
.
path
)
==
assetFilename
&&
entity
.
path
!=
assetPath
)
{
String
key
=
path
.
relative
(
entity
.
path
,
from:
baseAsset
.
base
);
String
assetEntry
;
if
(
baseAsset
.
symbolicPrefix
!=
null
)
assetEntry
=
path
.
join
(
baseAsset
.
symbolicPrefix
,
key
);
variants
.
add
(
new
_Asset
(
base:
baseAsset
.
base
,
assetEntry:
assetEntry
,
relativePath:
key
));
}
}
}
}
// Add assets referenced in the fonts section of the manifest.
if
(
manifestDescriptor
.
containsKey
(
'fonts'
))
{
for
(
Map
<
String
,
dynamic
>
family
in
manifestDescriptor
[
'fonts'
])
{
List
<
Map
<
String
,
dynamic
>>
fonts
=
family
[
'fonts'
];
if
(
fonts
==
null
)
continue
;
for
(
Map
<
String
,
dynamic
>
font
in
fonts
)
{
String
asset
=
font
[
'asset'
];
if
(
asset
==
null
)
continue
;
_Asset
baseAsset
=
_resolveAsset
(
packageMap
,
assetBase
,
asset
);
if
(!
baseAsset
.
assetFileExists
)
{
printError
(
'Error: unable to locate asset entry in flutter.yaml: "
$asset
".'
);
return
null
;
}
result
[
baseAsset
]
=
<
_Asset
>[];
}
}
}
return
result
;
}
_Asset
_resolveAsset
(
PackageMap
packageMap
,
String
assetBase
,
String
asset
)
{
if
(
asset
.
startsWith
(
'packages/'
)
&&
!
FileSystemEntity
.
isFileSync
(
path
.
join
(
assetBase
,
asset
)))
{
// Convert packages/flutter_gallery_assets/clouds-0.png to clouds-0.png.
String
packageKey
=
asset
.
substring
(
9
);
String
relativeAsset
=
asset
;
int
index
=
packageKey
.
indexOf
(
'/'
);
if
(
index
!=
-
1
)
{
relativeAsset
=
packageKey
.
substring
(
index
+
1
);
packageKey
=
packageKey
.
substring
(
0
,
index
);
}
Uri
uri
=
packageMap
.
map
[
packageKey
];
if
(
uri
!=
null
&&
uri
.
scheme
==
'file'
)
{
File
file
=
new
File
.
fromUri
(
uri
);
return
new
_Asset
(
base:
file
.
path
,
assetEntry:
asset
,
relativePath:
relativeAsset
);
}
}
return
new
_Asset
(
base:
assetBase
,
relativePath:
asset
);
}
dynamic
_loadFlutterYamlManifest
(
String
manifestPath
)
{
if
(
manifestPath
==
null
||
!
FileSystemEntity
.
isFileSync
(
manifestPath
))
return
null
;
String
manifestDescriptor
=
new
File
(
manifestPath
).
readAsStringSync
();
return
loadYaml
(
manifestDescriptor
);
}
Future
<
int
>
_validateFlutterYamlManifest
(
Object
manifest
)
async
{
String
schemaPath
=
path
.
join
(
path
.
absolute
(
Cache
.
flutterRoot
),
'packages'
,
'flutter_tools'
,
'schema'
,
'flutter_yaml.json'
);
Schema
schema
=
await
Schema
.
createSchemaFromUrl
(
'file://
$schemaPath
'
);
Validator
validator
=
new
Validator
(
schema
);
if
(
validator
.
validate
(
manifest
))
{
return
0
;
}
else
{
if
(
validator
.
errors
.
length
==
1
)
{
printError
(
'Error in flutter.yaml:
${validator.errors.first}
'
);
}
else
{
printError
(
'Error in flutter.yaml:'
);
printError
(
' '
+
validator
.
errors
.
join
(
'
\n
'
));
}
return
1
;
}
}
packages/flutter_tools/lib/src/devfs.dart
View file @
b6644733
...
...
@@ -9,23 +9,37 @@ import 'dart:io';
import
'package:path/path.dart'
as
path
;
import
'dart/package_map.dart'
;
import
'asset.dart'
;
import
'globals.dart'
;
import
'observatory.dart'
;
// A file that has been added to a DevFS.
class
DevFSEntry
{
DevFSEntry
(
this
.
devicePath
,
this
.
file
);
DevFSEntry
(
this
.
devicePath
,
this
.
file
)
:
bundleEntry
=
null
;
DevFSEntry
.
bundle
(
this
.
devicePath
,
AssetBundleEntry
bundleEntry
)
:
bundleEntry
=
bundleEntry
,
file
=
bundleEntry
.
file
;
final
String
devicePath
;
final
AssetBundleEntry
bundleEntry
;
final
File
file
;
FileStat
_fileStat
;
// When we updated the DevFS, did we see this entry?
bool
_wasSeen
=
false
;
DateTime
get
lastModified
=>
_fileStat
?.
modified
;
bool
get
stillExists
{
if
(
_isSourceEntry
)
return
true
;
_stat
();
return
_fileStat
.
type
!=
FileSystemEntityType
.
NOT_FOUND
;
}
bool
get
isModified
{
if
(
_isSourceEntry
)
return
true
;
if
(
_fileStat
==
null
)
{
_stat
();
return
true
;
...
...
@@ -36,8 +50,18 @@ class DevFSEntry {
}
void
_stat
()
{
if
(
_isSourceEntry
)
return
;
_fileStat
=
file
.
statSync
();
}
bool
get
_isSourceEntry
=>
file
==
null
;
Future
<
List
<
int
>>
contentsAsBytes
()
async
{
if
(
_isSourceEntry
)
return
bundleEntry
.
contentsAsBytes
();
return
file
.
readAsBytes
();
}
}
...
...
@@ -46,6 +70,7 @@ abstract class DevFSOperations {
Future
<
Uri
>
create
(
String
fsName
);
Future
<
dynamic
>
destroy
(
String
fsName
);
Future
<
dynamic
>
writeFile
(
String
fsName
,
DevFSEntry
entry
);
Future
<
dynamic
>
deleteFile
(
String
fsName
,
DevFSEntry
entry
);
Future
<
dynamic
>
writeSource
(
String
fsName
,
String
devicePath
,
String
contents
);
...
...
@@ -74,7 +99,7 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
Future
<
dynamic
>
writeFile
(
String
fsName
,
DevFSEntry
entry
)
async
{
List
<
int
>
bytes
;
try
{
bytes
=
await
entry
.
file
.
read
AsBytes
();
bytes
=
await
entry
.
contents
AsBytes
();
}
catch
(
e
)
{
return
e
;
}
...
...
@@ -91,6 +116,11 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
}
}
@override
Future
<
dynamic
>
deleteFile
(
String
fsName
,
DevFSEntry
entry
)
async
{
// TODO(johnmccutchan): Add file deletion to the devFS protocol.
}
@override
Future
<
dynamic
>
writeSource
(
String
fsName
,
String
devicePath
,
...
...
@@ -135,7 +165,11 @@ class DevFS {
return
await
_operations
.
destroy
(
fsName
);
}
Future
<
dynamic
>
update
()
async
{
Future
<
dynamic
>
update
([
AssetBundle
bundle
=
null
])
async
{
// Mark all entries as not seen.
_entries
.
forEach
((
String
path
,
DevFSEntry
entry
)
{
entry
.
_wasSeen
=
false
;
});
printTrace
(
'DevFS: Starting sync from
$rootDirectory
'
);
// Send the root and lib directories.
Directory
directory
=
rootDirectory
;
...
...
@@ -162,6 +196,27 @@ class DevFS {
}
}
}
if
(
bundle
!=
null
)
{
// Synchronize asset bundle.
for
(
AssetBundleEntry
entry
in
bundle
.
entries
)
{
// We write the assets into 'build/flx' so that they are in the
// same location in DevFS and the iOS simulator.
final
String
devicePath
=
path
.
join
(
'build/flx'
,
entry
.
archivePath
);
_syncBundleEntry
(
devicePath
,
entry
);
}
}
// Handle deletions.
final
List
<
String
>
toRemove
=
new
List
<
String
>();
_entries
.
forEach
((
String
path
,
DevFSEntry
entry
)
{
if
(!
entry
.
_wasSeen
)
{
_deleteEntry
(
path
,
entry
);
toRemove
.
add
(
path
);
}
});
for
(
int
i
=
0
;
i
<
toRemove
.
length
;
i
++)
{
_entries
.
remove
(
toRemove
[
i
]);
}
// Send the assets.
printTrace
(
'DevFS: Waiting for sync of
${_pendingWrites.length}
files '
'to finish'
);
await
Future
.
wait
(
_pendingWrites
);
...
...
@@ -175,6 +230,10 @@ class DevFS {
logger
.
flush
();
}
void
_deleteEntry
(
String
path
,
DevFSEntry
entry
)
{
_pendingWrites
.
add
(
_operations
.
deleteFile
(
fsName
,
entry
));
}
void
_syncFile
(
String
devicePath
,
File
file
)
{
DevFSEntry
entry
=
_entries
[
devicePath
];
if
(
entry
==
null
)
{
...
...
@@ -182,6 +241,7 @@ class DevFS {
entry
=
new
DevFSEntry
(
devicePath
,
file
);
_entries
[
devicePath
]
=
entry
;
}
entry
.
_wasSeen
=
true
;
bool
needsWrite
=
entry
.
isModified
;
if
(
needsWrite
)
{
Future
<
dynamic
>
pendingWrite
=
_operations
.
writeFile
(
fsName
,
entry
);
...
...
@@ -193,13 +253,29 @@ class DevFS {
}
}
bool
_shouldIgnore
(
String
path
)
{
void
_syncBundleEntry
(
String
devicePath
,
AssetBundleEntry
assetBundleEntry
)
{
DevFSEntry
entry
=
_entries
[
devicePath
];
if
(
entry
==
null
)
{
// New file.
entry
=
new
DevFSEntry
.
bundle
(
devicePath
,
assetBundleEntry
);
_entries
[
devicePath
]
=
entry
;
}
entry
.
_wasSeen
=
true
;
Future
<
dynamic
>
pendingWrite
=
_operations
.
writeFile
(
fsName
,
entry
);
if
(
pendingWrite
!=
null
)
{
_pendingWrites
.
add
(
pendingWrite
);
}
else
{
printTrace
(
'DevFS: Failed to sync "
$devicePath
"'
);
}
}
bool
_shouldIgnore
(
String
devicePath
)
{
List
<
String
>
ignoredPrefixes
=
<
String
>[
'android/'
,
'build/'
,
'ios/'
,
'packages/analyzer'
];
for
(
String
ignoredPrefix
in
ignoredPrefixes
)
{
if
(
p
ath
.
startsWith
(
ignoredPrefix
))
if
(
deviceP
ath
.
startsWith
(
ignoredPrefix
))
return
true
;
}
return
false
;
...
...
packages/flutter_tools/lib/src/flx.dart
View file @
b6644733
...
...
@@ -3,16 +3,13 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'package:json_schema/json_schema.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:yaml/yaml.dart'
;
import
'asset.dart'
;
import
'base/file_system.dart'
show
ensureDirectoryExists
;
import
'base/process.dart'
;
import
'cache.dart'
;
import
'dart/package_map.dart'
;
import
'globals.dart'
;
import
'toolchain.dart'
;
...
...
@@ -29,9 +26,6 @@ const String defaultWorkingDirPath = 'build/flx';
const
String
_kSnapshotKey
=
'snapshot_blob.bin'
;
const
String
_kFontSetMaterial
=
'material'
;
const
String
_kFontSetRoboto
=
'roboto'
;
Future
<
int
>
createSnapshot
({
String
mainPath
,
String
snapshotPath
,
...
...
@@ -54,293 +48,6 @@ Future<int> createSnapshot({
return
runCommandAndStreamOutput
(
args
);
}
class
_Asset
{
_Asset
({
this
.
base
,
String
assetEntry
,
this
.
relativePath
,
this
.
source
})
{
this
.
_assetEntry
=
assetEntry
;
}
String
_assetEntry
;
final
String
base
;
/// The entry to list in the generated asset manifest.
String
get
assetEntry
=>
_assetEntry
??
relativePath
;
/// Where the resource is on disk relative to [base].
final
String
relativePath
;
final
String
source
;
File
get
assetFile
{
return
new
File
(
source
!=
null
?
'
$base
/
$source
'
:
'
$base
/
$relativePath
'
);
}
bool
get
assetFileExists
=>
assetFile
.
existsSync
();
/// The delta between what the assetEntry is and the relativePath (e.g.,
/// packages/flutter_gallery).
String
get
symbolicPrefix
{
if
(
_assetEntry
==
null
||
_assetEntry
==
relativePath
)
return
null
;
int
index
=
_assetEntry
.
indexOf
(
relativePath
);
return
index
==
-
1
?
null
:
_assetEntry
.
substring
(
0
,
index
);
}
@override
String
toString
()
=>
'asset:
$assetEntry
'
;
}
Map
<
String
,
dynamic
>
_readMaterialFontsManifest
()
{
String
fontsPath
=
path
.
join
(
path
.
absolute
(
Cache
.
flutterRoot
),
'packages'
,
'flutter_tools'
,
'schema'
,
'material_fonts.yaml'
);
return
loadYaml
(
new
File
(
fontsPath
).
readAsStringSync
());
}
final
Map
<
String
,
dynamic
>
_materialFontsManifest
=
_readMaterialFontsManifest
();
List
<
Map
<
String
,
dynamic
>>
_getMaterialFonts
(
String
fontSet
)
{
return
_materialFontsManifest
[
fontSet
];
}
List
<
_Asset
>
_getMaterialAssets
(
String
fontSet
)
{
List
<
_Asset
>
result
=
<
_Asset
>[];
for
(
Map
<
String
,
dynamic
>
family
in
_getMaterialFonts
(
fontSet
))
{
for
(
Map
<
String
,
dynamic
>
font
in
family
[
'fonts'
])
{
String
assetKey
=
font
[
'asset'
];
result
.
add
(
new
_Asset
(
base:
'
${Cache.flutterRoot}
/bin/cache/artifacts/material_fonts'
,
source
:
path
.
basename
(
assetKey
),
relativePath:
assetKey
));
}
}
return
result
;
}
/// Given an assetBase location and a flutter.yaml manifest, return a map of
/// assets to asset variants.
///
/// Returns `null` on missing assets.
Map
<
_Asset
,
List
<
_Asset
>>
_parseAssets
(
PackageMap
packageMap
,
Map
<
String
,
dynamic
>
manifestDescriptor
,
String
assetBase
,
{
List
<
String
>
excludeDirs:
const
<
String
>[]
})
{
Map
<
_Asset
,
List
<
_Asset
>>
result
=
<
_Asset
,
List
<
_Asset
>>{};
if
(
manifestDescriptor
==
null
)
return
result
;
excludeDirs
=
excludeDirs
.
map
(
(
String
exclude
)
=>
path
.
absolute
(
exclude
)
+
Platform
.
pathSeparator
).
toList
();
if
(
manifestDescriptor
.
containsKey
(
'assets'
))
{
for
(
String
asset
in
manifestDescriptor
[
'assets'
])
{
_Asset
baseAsset
=
_resolveAsset
(
packageMap
,
assetBase
,
asset
);
if
(!
baseAsset
.
assetFileExists
)
{
printError
(
'Error: unable to locate asset entry in flutter.yaml: "
$asset
".'
);
return
null
;
}
List
<
_Asset
>
variants
=
<
_Asset
>[];
result
[
baseAsset
]
=
variants
;
// Find asset variants
String
assetPath
=
baseAsset
.
assetFile
.
path
;
String
assetFilename
=
path
.
basename
(
assetPath
);
Directory
assetDir
=
new
Directory
(
path
.
dirname
(
assetPath
));
List
<
FileSystemEntity
>
files
=
assetDir
.
listSync
(
recursive:
true
);
for
(
FileSystemEntity
entity
in
files
)
{
if
(!
FileSystemEntity
.
isFileSync
(
entity
.
path
))
continue
;
// Exclude any files in the given directories.
if
(
excludeDirs
.
any
((
String
exclude
)
=>
entity
.
path
.
startsWith
(
exclude
)))
continue
;
if
(
path
.
basename
(
entity
.
path
)
==
assetFilename
&&
entity
.
path
!=
assetPath
)
{
String
key
=
path
.
relative
(
entity
.
path
,
from:
baseAsset
.
base
);
String
assetEntry
;
if
(
baseAsset
.
symbolicPrefix
!=
null
)
assetEntry
=
path
.
join
(
baseAsset
.
symbolicPrefix
,
key
);
variants
.
add
(
new
_Asset
(
base:
baseAsset
.
base
,
assetEntry:
assetEntry
,
relativePath:
key
));
}
}
}
}
// Add assets referenced in the fonts section of the manifest.
if
(
manifestDescriptor
.
containsKey
(
'fonts'
))
{
for
(
Map
<
String
,
dynamic
>
family
in
manifestDescriptor
[
'fonts'
])
{
List
<
Map
<
String
,
dynamic
>>
fonts
=
family
[
'fonts'
];
if
(
fonts
==
null
)
continue
;
for
(
Map
<
String
,
dynamic
>
font
in
fonts
)
{
String
asset
=
font
[
'asset'
];
if
(
asset
==
null
)
continue
;
_Asset
baseAsset
=
_resolveAsset
(
packageMap
,
assetBase
,
asset
);
if
(!
baseAsset
.
assetFileExists
)
{
printError
(
'Error: unable to locate asset entry in flutter.yaml: "
$asset
".'
);
return
null
;
}
result
[
baseAsset
]
=
<
_Asset
>[];
}
}
}
return
result
;
}
final
String
_licenseSeparator
=
'
\n
'
+
(
'-'
*
80
)
+
'
\n
'
;
/// Returns a ZipEntry representing the license file.
Future
<
ZipEntry
>
_obtainLicenses
(
PackageMap
packageMap
,
String
assetBase
)
async
{
// Read the LICENSE file from each package in the .packages file,
// splitting each one into each component license (so that we can
// de-dupe if possible).
// For the sky_engine package we assume each license starts with
// package names. For the other packages we assume that each
// license is raw.
final
Map
<
String
,
Set
<
String
>>
packageLicenses
=
<
String
,
Set
<
String
>>{};
for
(
String
packageName
in
packageMap
.
map
.
keys
)
{
final
Uri
package
=
packageMap
.
map
[
packageName
];
if
(
package
!=
null
&&
package
.
scheme
==
'file'
)
{
final
File
file
=
new
File
.
fromUri
(
package
.
resolve
(
'../LICENSE'
));
if
(
file
.
existsSync
())
{
final
List
<
String
>
rawLicenses
=
(
await
file
.
readAsString
()).
split
(
_licenseSeparator
);
for
(
String
rawLicense
in
rawLicenses
)
{
String
licenseText
;
List
<
String
>
packageNames
;
if
(
packageName
==
'sky_engine'
)
{
final
int
split
=
rawLicense
.
indexOf
(
'
\n\n
'
);
if
(
split
>=
0
)
{
packageNames
=
rawLicense
.
substring
(
0
,
split
).
split
(
'
\n
'
);
licenseText
=
rawLicense
.
substring
(
split
+
2
);
}
}
if
(
licenseText
==
null
)
{
licenseText
=
rawLicense
;
packageNames
=
<
String
>[
packageName
];
}
packageLicenses
.
putIfAbsent
(
rawLicense
,
()
=>
new
Set
<
String
>())
..
addAll
(
packageNames
);
}
}
}
}
final
List
<
String
>
combinedLicensesList
=
packageLicenses
.
keys
.
map
(
(
String
license
)
{
List
<
String
>
packageNames
=
packageLicenses
[
license
].
toList
()
..
sort
();
return
packageNames
.
join
(
'
\n
'
)
+
'
\n\n
'
+
license
;
}
).
toList
();
combinedLicensesList
.
sort
();
final
String
combinedLicenses
=
combinedLicensesList
.
join
(
_licenseSeparator
);
return
new
ZipEntry
.
fromString
(
'LICENSE'
,
combinedLicenses
);
}
_Asset
_resolveAsset
(
PackageMap
packageMap
,
String
assetBase
,
String
asset
)
{
if
(
asset
.
startsWith
(
'packages/'
)
&&
!
FileSystemEntity
.
isFileSync
(
path
.
join
(
assetBase
,
asset
)))
{
// Convert packages/flutter_gallery_assets/clouds-0.png to clouds-0.png.
String
packageKey
=
asset
.
substring
(
9
);
String
relativeAsset
=
asset
;
int
index
=
packageKey
.
indexOf
(
'/'
);
if
(
index
!=
-
1
)
{
relativeAsset
=
packageKey
.
substring
(
index
+
1
);
packageKey
=
packageKey
.
substring
(
0
,
index
);
}
Uri
uri
=
packageMap
.
map
[
packageKey
];
if
(
uri
!=
null
&&
uri
.
scheme
==
'file'
)
{
File
file
=
new
File
.
fromUri
(
uri
);
return
new
_Asset
(
base:
file
.
path
,
assetEntry:
asset
,
relativePath:
relativeAsset
);
}
}
return
new
_Asset
(
base:
assetBase
,
relativePath:
asset
);
}
dynamic
_loadManifest
(
String
manifestPath
)
{
if
(
manifestPath
==
null
||
!
FileSystemEntity
.
isFileSync
(
manifestPath
))
return
null
;
String
manifestDescriptor
=
new
File
(
manifestPath
).
readAsStringSync
();
return
loadYaml
(
manifestDescriptor
);
}
Future
<
int
>
_validateManifest
(
Object
manifest
)
async
{
String
schemaPath
=
path
.
join
(
path
.
absolute
(
Cache
.
flutterRoot
),
'packages'
,
'flutter_tools'
,
'schema'
,
'flutter_yaml.json'
);
Schema
schema
=
await
Schema
.
createSchemaFromUrl
(
'file://
$schemaPath
'
);
Validator
validator
=
new
Validator
(
schema
);
if
(
validator
.
validate
(
manifest
))
{
return
0
;
}
else
{
if
(
validator
.
errors
.
length
==
1
)
{
printError
(
'Error in flutter.yaml:
${validator.errors.first}
'
);
}
else
{
printError
(
'Error in flutter.yaml:'
);
printError
(
' '
+
validator
.
errors
.
join
(
'
\n
'
));
}
return
1
;
}
}
/// Create a [ZipEntry] from the given [_Asset]; the asset must exist.
ZipEntry
_createAssetEntry
(
_Asset
asset
)
{
assert
(
asset
.
assetFileExists
);
return
new
ZipEntry
.
fromFile
(
asset
.
assetEntry
,
asset
.
assetFile
);
}
ZipEntry
_createAssetManifest
(
Map
<
_Asset
,
List
<
_Asset
>>
assetVariants
)
{
Map
<
String
,
List
<
String
>>
json
=
<
String
,
List
<
String
>>{};
for
(
_Asset
main
in
assetVariants
.
keys
)
{
List
<
String
>
variants
=
<
String
>[];
for
(
_Asset
variant
in
assetVariants
[
main
])
variants
.
add
(
variant
.
relativePath
);
json
[
main
.
relativePath
]
=
variants
;
}
return
new
ZipEntry
.
fromString
(
'AssetManifest.json'
,
JSON
.
encode
(
json
));
}
ZipEntry
_createFontManifest
(
Map
<
String
,
dynamic
>
manifestDescriptor
,
bool
usesMaterialDesign
,
bool
includeRobotoFonts
)
{
List
<
Map
<
String
,
dynamic
>>
fonts
=
<
Map
<
String
,
dynamic
>>[];
if
(
usesMaterialDesign
)
{
fonts
.
addAll
(
_getMaterialFonts
(
_kFontSetMaterial
));
if
(
includeRobotoFonts
)
fonts
.
addAll
(
_getMaterialFonts
(
_kFontSetRoboto
));
}
if
(
manifestDescriptor
!=
null
&&
manifestDescriptor
.
containsKey
(
'fonts'
))
fonts
.
addAll
(
manifestDescriptor
[
'fonts'
]);
if
(
fonts
.
isEmpty
)
return
null
;
return
new
ZipEntry
.
fromString
(
'FontManifest.json'
,
JSON
.
encode
(
fonts
));
}
/// Build the flx in the build/ directory and return `localBundlePath` on success.
///
/// Return `null` on failure.
...
...
@@ -362,19 +69,6 @@ Future<String> buildFlx({
return
result
==
0
?
localBundlePath
:
null
;
}
/// The result from [buildInTempDir]. Note that this object should be disposed after use.
class
DirectoryResult
{
DirectoryResult
(
this
.
directory
,
this
.
localBundlePath
);
final
Directory
directory
;
final
String
localBundlePath
;
/// Call this to delete the temporary directory.
void
dispose
()
{
directory
.
deleteSync
(
recursive:
true
);
}
}
Future
<
int
>
build
({
String
mainPath:
defaultMainPath
,
String
manifestPath:
defaultManifestPath
,
...
...
@@ -386,16 +80,6 @@ Future<int> build({
bool
precompiledSnapshot:
false
,
bool
includeRobotoFonts:
true
})
async
{
Object
manifest
=
_loadManifest
(
manifestPath
);
if
(
manifest
!=
null
)
{
int
result
=
await
_validateManifest
(
manifest
);
if
(
result
!=
0
)
return
result
;
}
Map
<
String
,
dynamic
>
manifestDescriptor
=
manifest
;
String
assetBasePath
=
path
.
dirname
(
path
.
absolute
(
manifestPath
));
File
snapshotFile
;
if
(!
precompiledSnapshot
)
{
...
...
@@ -417,9 +101,8 @@ Future<int> build({
}
return
assemble
(
manifest
Descriptor:
manifestDescriptor
,
manifest
Path:
manifestPath
,
snapshotFile:
snapshotFile
,
assetBasePath:
assetBasePath
,
outputPath:
outputPath
,
privateKeyPath:
privateKeyPath
,
workingDirPath:
workingDirPath
,
...
...
@@ -428,9 +111,8 @@ Future<int> build({
}
Future
<
int
>
assemble
({
Map
<
String
,
dynamic
>
manifestDescriptor:
const
<
String
,
dynamic
>{}
,
String
manifestPath
,
File
snapshotFile
,
String
assetBasePath:
defaultAssetBasePath
,
String
outputPath:
defaultFlxOutputPath
,
String
privateKeyPath:
defaultPrivateKeyPath
,
String
workingDirPath:
defaultWorkingDirPath
,
...
...
@@ -438,61 +120,22 @@ Future<int> assemble({
})
async
{
printTrace
(
'Building
$outputPath
'
);
final
PackageMap
packageMap
=
new
PackageMap
(
path
.
join
(
assetBasePath
,
'.packages'
));
Map
<
_Asset
,
List
<
_Asset
>>
assetVariants
=
_parseAssets
(
packageMap
,
manifestDescriptor
,
assetBasePath
,
excludeDirs:
<
String
>[
workingDirPath
,
path
.
join
(
assetBasePath
,
'build'
)]
);
if
(
assetVariants
==
null
)
return
1
;
final
bool
usesMaterialDesign
=
manifestDescriptor
!=
null
&&
manifestDescriptor
[
'uses-material-design'
]
==
true
;
ZipBuilder
zipBuilder
=
new
ZipBuilder
();
if
(
snapshotFile
!=
null
)
zipBuilder
.
addEntry
(
new
ZipEntry
.
fromFile
(
_kSnapshotKey
,
snapshotFile
));
for
(
_Asset
asset
in
assetVariants
.
keys
)
{
ZipEntry
assetEntry
=
_createAssetEntry
(
asset
);
if
(
assetEntry
==
null
)
return
1
;
zipBuilder
.
addEntry
(
assetEntry
);
for
(
_Asset
variant
in
assetVariants
[
asset
])
{
ZipEntry
variantEntry
=
_createAssetEntry
(
variant
);
if
(
variantEntry
==
null
)
return
1
;
zipBuilder
.
addEntry
(
variantEntry
);
}
}
List
<
_Asset
>
materialAssets
=
<
_Asset
>[];
if
(
usesMaterialDesign
)
{
materialAssets
.
addAll
(
_getMaterialAssets
(
_kFontSetMaterial
));
if
(
includeRobotoFonts
)
materialAssets
.
addAll
(
_getMaterialAssets
(
_kFontSetRoboto
));
}
for
(
_Asset
asset
in
materialAssets
)
{
ZipEntry
assetEntry
=
_createAssetEntry
(
asset
);
if
(
assetEntry
==
null
)
return
1
;
zipBuilder
.
addEntry
(
assetEntry
);
// Build the asset bundle.
AssetBundle
assetBundle
=
new
AssetBundle
();
int
result
=
await
assetBundle
.
build
(
manifestPath:
manifestPath
,
workingDirPath:
workingDirPath
,
includeRobotoFonts:
includeRobotoFonts
);
if
(
result
!=
0
)
{
return
result
;
}
zipBuilder
.
addEntry
(
_createAssetManifest
(
assetVariants
)
);
ZipBuilder
zipBuilder
=
new
ZipBuilder
(
);
ZipEntry
fontManifest
=
_createFontManifest
(
manifestDescriptor
,
usesMaterialDesign
,
includeRobotoFonts
);
if
(
fontManifest
!=
null
)
zipBuilder
.
addEntry
(
fontManifest
);
// Add all entries from the asset bundle.
zipBuilder
.
entries
.
addAll
(
assetBundle
.
entries
);
// TODO(ianh): Only do the following line if we've changed packages
zipBuilder
.
addEntry
(
await
_obtainLicenses
(
packageMap
,
assetBasePath
));
if
(
snapshotFile
!=
null
)
zipBuilder
.
addEntry
(
new
AssetBundleEntry
.
fromFile
(
_kSnapshotKey
,
snapshotFile
));
ensureDirectoryExists
(
outputPath
);
...
...
packages/flutter_tools/lib/src/zip.dart
View file @
b6644733
...
...
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:convert'
show
UTF8
;
import
'dart:io'
;
import
'package:archive/archive.dart'
;
import
'package:path/path.dart'
as
path
;
import
'asset.dart'
;
import
'base/process.dart'
;
abstract
class
ZipBuilder
{
...
...
@@ -21,30 +21,13 @@ abstract class ZipBuilder {
ZipBuilder
.
_
();
List
<
ZipEntry
>
entries
=
<
Zip
Entry
>[];
List
<
AssetBundleEntry
>
entries
=
<
AssetBundle
Entry
>[];
void
addEntry
(
Zip
Entry
entry
)
=>
entries
.
add
(
entry
);
void
addEntry
(
AssetBundle
Entry
entry
)
=>
entries
.
add
(
entry
);
void
createZip
(
File
outFile
,
Directory
zipBuildDir
);
}
class
ZipEntry
{
ZipEntry
.
fromFile
(
this
.
archivePath
,
File
file
)
{
this
.
_file
=
file
;
}
ZipEntry
.
fromString
(
this
.
archivePath
,
String
contents
)
{
this
.
_contents
=
contents
;
}
final
String
archivePath
;
File
_file
;
String
_contents
;
bool
get
isStringEntry
=>
_contents
!=
null
;
}
class
_ArchiveZipBuilder
extends
ZipBuilder
{
_ArchiveZipBuilder
()
:
super
.
_
();
...
...
@@ -52,14 +35,9 @@ class _ArchiveZipBuilder extends ZipBuilder {
void
createZip
(
File
outFile
,
Directory
zipBuildDir
)
{
Archive
archive
=
new
Archive
();
for
(
ZipEntry
entry
in
entries
)
{
if
(
entry
.
isStringEntry
)
{
List
<
int
>
data
=
UTF8
.
encode
(
entry
.
_contents
);
for
(
AssetBundleEntry
entry
in
entries
)
{
List
<
int
>
data
=
entry
.
contentsAsBytes
();
archive
.
addFile
(
new
ArchiveFile
.
noCompress
(
entry
.
archivePath
,
data
.
length
,
data
));
}
else
{
List
<
int
>
data
=
entry
.
_file
.
readAsBytesSync
();
archive
.
addFile
(
new
ArchiveFile
(
entry
.
archivePath
,
data
.
length
,
data
));
}
}
List
<
int
>
zipData
=
new
ZipEncoder
().
encode
(
archive
);
...
...
@@ -79,18 +57,11 @@ class _ZipToolBuilder extends ZipBuilder {
zipBuildDir
.
deleteSync
(
recursive:
true
);
zipBuildDir
.
createSync
(
recursive:
true
);
for
(
ZipEntry
entry
in
entries
)
{
if
(
entry
.
isStringEntry
)
{
List
<
int
>
data
=
UTF8
.
encode
(
entry
.
_contents
);
for
(
AssetBundleEntry
entry
in
entries
)
{
List
<
int
>
data
=
entry
.
contentsAsBytes
();
File
file
=
new
File
(
path
.
join
(
zipBuildDir
.
path
,
entry
.
archivePath
));
file
.
parent
.
createSync
(
recursive:
true
);
file
.
writeAsBytesSync
(
data
);
}
else
{
List
<
int
>
data
=
entry
.
_file
.
readAsBytesSync
();
File
file
=
new
File
(
path
.
join
(
zipBuildDir
.
path
,
entry
.
archivePath
));
file
.
parent
.
createSync
(
recursive:
true
);
file
.
writeAsBytesSync
(
data
);
}
}
if
(
_getCompressedNames
().
isNotEmpty
)
{
...
...
@@ -112,13 +83,13 @@ class _ZipToolBuilder extends ZipBuilder {
Iterable
<
String
>
_getCompressedNames
()
{
return
entries
.
where
((
Zip
Entry
entry
)
=>
!
entry
.
isStringEntry
)
.
map
((
Zip
Entry
entry
)
=>
entry
.
archivePath
);
.
where
((
AssetBundle
Entry
entry
)
=>
!
entry
.
isStringEntry
)
.
map
((
AssetBundle
Entry
entry
)
=>
entry
.
archivePath
);
}
Iterable
<
String
>
_getStoredNames
()
{
return
entries
.
where
((
Zip
Entry
entry
)
=>
entry
.
isStringEntry
)
.
map
((
Zip
Entry
entry
)
=>
entry
.
archivePath
);
.
where
((
AssetBundle
Entry
entry
)
=>
entry
.
isStringEntry
)
.
map
((
AssetBundle
Entry
entry
)
=>
entry
.
archivePath
);
}
}
packages/flutter_tools/test/devfs_test.dart
View file @
b6644733
...
...
@@ -4,6 +4,7 @@
import
'dart:io'
;
import
'package:flutter_tools/src/asset.dart'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:test/test.dart'
;
...
...
@@ -18,6 +19,8 @@ void main() {
String
basePath
;
MockDevFSOperations
devFSOperations
=
new
MockDevFSOperations
();
DevFS
devFS
;
AssetBundle
assetBundle
=
new
AssetBundle
();
assetBundle
.
entries
.
add
(
new
AssetBundleEntry
.
fromString
(
'a.txt'
,
''
));
group
(
'devfs'
,
()
{
testUsingContext
(
'create local file system'
,
()
async
{
tempDir
=
Directory
.
systemTemp
.
createTempSync
();
...
...
@@ -38,8 +41,6 @@ void main() {
testUsingContext
(
'modify existing file on local file system'
,
()
async
{
File
file
=
new
File
(
path
.
join
(
basePath
,
filePath
));
file
.
writeAsBytesSync
(<
int
>[
1
,
2
,
3
,
4
,
5
,
6
]);
});
testUsingContext
(
'update dev file system'
,
()
async
{
await
devFS
.
update
();
expect
(
devFSOperations
.
contains
(
'writeFile test bar/foo.txt'
),
isTrue
);
});
...
...
@@ -47,11 +48,29 @@ void main() {
File
file
=
new
File
(
path
.
join
(
basePath
,
filePath2
));
await
file
.
parent
.
create
(
recursive:
true
);
file
.
writeAsBytesSync
(<
int
>[
1
,
2
,
3
,
4
,
5
,
6
,
7
]);
});
testUsingContext
(
'update dev file system'
,
()
async
{
await
devFS
.
update
();
expect
(
devFSOperations
.
contains
(
'writeFile test foo/bar.txt'
),
isTrue
);
});
testUsingContext
(
'delete a file from the local file system'
,
()
async
{
File
file
=
new
File
(
path
.
join
(
basePath
,
filePath
));
await
file
.
delete
();
await
devFS
.
update
();
expect
(
devFSOperations
.
contains
(
'deleteFile test bar/foo.txt'
),
isTrue
);
});
testUsingContext
(
'add file in an asset bundle'
,
()
async
{
await
devFS
.
update
(
assetBundle
);
expect
(
devFSOperations
.
contains
(
'writeFile test build/flx/a.txt'
),
isTrue
);
});
testUsingContext
(
'add a file to the asset bundle'
,
()
async
{
assetBundle
.
entries
.
add
(
new
AssetBundleEntry
.
fromString
(
'b.txt'
,
''
));
await
devFS
.
update
(
assetBundle
);
expect
(
devFSOperations
.
contains
(
'writeFile test build/flx/b.txt'
),
isTrue
);
});
testUsingContext
(
'delete a file from the asset bundle'
,
()
async
{
assetBundle
.
entries
.
clear
();
await
devFS
.
update
(
assetBundle
);
expect
(
devFSOperations
.
contains
(
'deleteFile test build/flx/b.txt'
),
isTrue
);
});
testUsingContext
(
'delete dev file system'
,
()
async
{
await
devFS
.
destroy
();
});
...
...
packages/flutter_tools/test/src/mocks.dart
View file @
b6644733
...
...
@@ -75,6 +75,8 @@ class MockDevFSOperations implements DevFSOperations {
final
List
<
String
>
messages
=
new
List
<
String
>();
bool
contains
(
String
match
)
{
print
(
'Checking for `
$match
` in:'
);
print
(
messages
);
bool
result
=
messages
.
contains
(
match
);
messages
.
clear
();
return
result
;
...
...
@@ -96,6 +98,11 @@ class MockDevFSOperations implements DevFSOperations {
messages
.
add
(
'writeFile
$fsName
${entry.devicePath}
'
);
}
@override
Future
<
dynamic
>
deleteFile
(
String
fsName
,
DevFSEntry
entry
)
async
{
messages
.
add
(
'deleteFile
$fsName
${entry.devicePath}
'
);
}
@override
Future
<
dynamic
>
writeSource
(
String
fsName
,
String
devicePath
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment