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
c91b6571
Unverified
Commit
c91b6571
authored
Jun 07, 2019
by
Jonah Williams
Committed by
GitHub
Jun 07, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Codegen an entrypoint for flutter web applications (#33956)
parent
4e5cf5ef
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
321 additions
and
232 deletions
+321
-232
main.dart
dev/integration_tests/web/lib/main.dart
+1
-3
main_web.dart
examples/flutter_gallery/lib/main_web.dart
+0
-16
web_compilation_delegate.dart
..._tools/lib/src/build_runner/web_compilation_delegate.dart
+251
-17
build_web.dart
packages/flutter_tools/lib/src/commands/build_web.dart
+3
-29
context_runner.dart
packages/flutter_tools/lib/src/context_runner.dart
+0
-2
project.dart
packages/flutter_tools/lib/src/project.dart
+3
-0
asset_server.dart
packages/flutter_tools/lib/src/web/asset_server.dart
+4
-3
compile.dart
packages/flutter_tools/lib/src/web/compile.dart
+50
-67
web_device.dart
packages/flutter_tools/lib/src/web/web_device.dart
+9
-22
compile_test.dart
packages/flutter_tools/test/web/compile_test.dart
+0
-50
devices_test.dart
packages/flutter_tools/test/web/devices_test.dart
+0
-23
No files found.
dev/integration_tests/web/lib/main.dart
View file @
c91b6571
...
...
@@ -6,8 +6,6 @@ import 'package:flutter/widgets.dart';
void
main
(
)
{
runApp
(
Center
(
// Can remove when https://github.com/dart-lang/sdk/issues/35801 is fixed.
// ignore: prefer_const_constructors
child:
Text
(
'Hello, World'
,
textDirection:
TextDirection
.
ltr
),
child:
const
Text
(
'Hello, World'
,
textDirection:
TextDirection
.
ltr
),
));
}
examples/flutter_gallery/lib/main_web.dart
deleted
100644 → 0
View file @
4e5cf5ef
// Copyright 2019 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.
// Thanks for checking out Flutter!
// Like what you see? Tweet us @FlutterDev
import
'dart:ui'
as
ui
;
import
'package:flutter/material.dart'
;
import
'gallery/app.dart'
;
Future
<
void
>
main
()
async
{
await
ui
.
webOnlyInitializePlatform
();
// ignore: undefined_function
runApp
(
const
GalleryApp
());
}
packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart
View file @
c91b6571
...
...
@@ -3,13 +3,19 @@
// found in the LICENSE file.
// ignore_for_file: implementation_imports
import
'dart:async'
;
import
'dart:io'
as
io
;
// ignore: dart_io_import
import
'package:build/build.dart'
;
import
'package:build_config/build_config.dart'
;
import
'package:build_modules/build_modules.dart'
;
import
'package:build_modules/builders.dart'
;
import
'package:build_modules/src/module_builder.dart'
;
import
'package:build_modules/src/platform.dart'
;
import
'package:build_modules/src/workers.dart'
;
import
'package:build_runner_core/build_runner_core.dart'
as
core
;
import
'package:build_runner_core/src/asset_graph/graph.dart'
;
import
'package:build_runner_core/src/asset_graph/node.dart'
;
import
'package:build_runner_core/src/generate/build_impl.dart'
;
import
'package:build_runner_core/src/generate/options.dart'
;
import
'package:build_test/builder.dart'
;
...
...
@@ -17,9 +23,11 @@ import 'package:build_test/src/debug_test_builder.dart';
import
'package:build_web_compilers/build_web_compilers.dart'
;
import
'package:build_web_compilers/builders.dart'
;
import
'package:build_web_compilers/src/dev_compiler_bootstrap.dart'
;
import
'package:crypto/crypto.dart'
;
import
'package:logging/logging.dart'
;
import
'package:meta/meta.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:scratch_space/scratch_space.dart'
;
import
'package:test_core/backend.dart'
;
import
'package:watcher/watcher.dart'
;
...
...
@@ -28,6 +36,7 @@ import '../base/file_system.dart';
import
'../base/logger.dart'
;
import
'../base/platform.dart'
;
import
'../compile.dart'
;
import
'../convert.dart'
;
import
'../dart/package_map.dart'
;
import
'../globals.dart'
;
import
'../web/compile.dart'
;
...
...
@@ -83,6 +92,22 @@ final List<core.BuilderApplication> builders = <core.BuilderApplication>[
],
),
),
core
.
apply
(
'flutter_tools|shell'
,
<
BuilderFactory
>[
(
BuilderOptions
options
)
=>
FlutterWebShellBuilder
(
options
.
config
[
'targets'
]
??
<
String
>[
'lib/main.dart'
]
),
],
core
.
toRoot
(),
hideOutput:
true
,
defaultGenerateFor:
const
InputSet
(
include:
<
String
>[
'lib/**'
,
'web/**'
,
],
),
),
core
.
apply
(
'flutter_tools|module_library'
,
<
Builder
Function
(
BuilderOptions
)>[
moduleLibraryBuilder
],
...
...
@@ -127,7 +152,9 @@ final List<core.BuilderApplication> builders = <core.BuilderApplication>[
'flutter_tools|entrypoint'
,
<
BuilderFactory
>[
(
BuilderOptions
options
)
=>
FlutterWebEntrypointBuilder
(
options
.
config
[
'targets'
]
??
<
String
>[
'lib/main.dart'
]),
options
.
config
[
'targets'
]
??
<
String
>[
'lib/main.dart'
],
options
.
config
[
'release'
],
),
],
core
.
toRoot
(),
hideOutput:
true
,
...
...
@@ -152,11 +179,15 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
PackageUriMapper
_packageUriMapper
;
@override
Future
<
void
>
initialize
({
Future
<
bool
>
initialize
({
@required
Directory
projectDirectory
,
@required
List
<
String
>
targets
,
String
testOutputDir
,
bool
release
=
false
,
})
async
{
// Create the .dart_tool directory if it doesn't exist.
projectDirectory
.
childDirectory
(
'.dart_tool'
).
createSync
();
// Override the generated output directory so this does not conflict with
// other build_runner output.
core
.
overrideGeneratedOutputDirectory
(
'flutter_web'
);
...
...
@@ -195,19 +226,36 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
};
final
Status
status
=
logger
.
startProgress
(
'Compiling
${targets.first}
for the Web...'
,
timeout:
null
);
core
.
BuildResult
result
;
try
{
_builder
=
await
BuildImpl
.
create
(
result
=
await
_runBuilder
(
buildEnvironment
,
buildOptions
,
targets
,
release
,
buildDirs
,
);
return
result
.
status
==
core
.
BuildStatus
.
success
;
}
on
core
.
BuildConfigChangedException
{
await
_cleanAssets
(
projectDirectory
);
result
=
await
_runBuilder
(
buildEnvironment
,
builders
,
<
String
,
Map
<
String
,
dynamic
>>{
'flutter_tools|entrypoint'
:
<
String
,
dynamic
>{
'targets'
:
targets
,
}
},
isReleaseBuild:
false
,
buildOptions
,
targets
,
release
,
buildDirs
,
);
return
result
.
status
==
core
.
BuildStatus
.
success
;
}
on
core
.
BuildScriptChangedException
{
await
_cleanAssets
(
projectDirectory
);
result
=
await
_runBuilder
(
buildEnvironment
,
buildOptions
,
targets
,
release
,
buildDirs
,
);
await
_builder
.
run
(
const
<
AssetId
,
ChangeType
>{},
buildDirs:
buildDirs
)
;
return
result
.
status
==
core
.
BuildStatus
.
success
;
}
finally
{
status
.
stop
();
}
...
...
@@ -219,9 +267,8 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
logger
.
startProgress
(
'Recompiling sources...'
,
timeout:
null
);
final
Map
<
AssetId
,
ChangeType
>
updates
=
<
AssetId
,
ChangeType
>{};
for
(
Uri
input
in
inputs
)
{
updates
[
AssetId
.
resolve
(
_packageUriMapper
.
map
(
input
.
toFilePath
()).
toString
())]
=
ChangeType
.
MODIFY
;
final
AssetId
assetId
=
AssetId
.
resolve
(
_packageUriMapper
.
map
(
input
.
toFilePath
()).
toString
());
updates
[
assetId
]
=
ChangeType
.
MODIFY
;
}
core
.
BuildResult
result
;
try
{
...
...
@@ -231,13 +278,81 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
}
return
result
.
status
==
core
.
BuildStatus
.
success
;
}
Future
<
core
.
BuildResult
>
_runBuilder
(
core
.
BuildEnvironment
buildEnvironment
,
BuildOptions
buildOptions
,
List
<
String
>
targets
,
bool
release
,
Set
<
core
.
BuildDirectory
>
buildDirs
)
async
{
_builder
=
await
BuildImpl
.
create
(
buildOptions
,
buildEnvironment
,
builders
,
<
String
,
Map
<
String
,
dynamic
>>{
'flutter_tools|entrypoint'
:
<
String
,
dynamic
>{
'targets'
:
targets
,
'release'
:
release
,
},
'flutter_tools|shell'
:
<
String
,
dynamic
>{
'targets'
:
targets
,
}
},
isReleaseBuild:
false
,
);
return
_builder
.
run
(
const
<
AssetId
,
ChangeType
>{},
buildDirs:
buildDirs
,
);
}
Future
<
void
>
_cleanAssets
(
Directory
projectDirectory
)
async
{
final
File
assetGraphFile
=
fs
.
file
(
core
.
assetGraphPath
);
AssetGraph
assetGraph
;
try
{
assetGraph
=
AssetGraph
.
deserialize
(
await
assetGraphFile
.
readAsBytes
());
}
catch
(
_
)
{
printTrace
(
'Failed to clean up asset graph.'
);
}
final
core
.
PackageGraph
packageGraph
=
core
.
PackageGraph
.
forThisPackage
();
await
_cleanUpSourceOutputs
(
assetGraph
,
packageGraph
);
final
Directory
cacheDirectory
=
fs
.
directory
(
fs
.
path
.
join
(
projectDirectory
.
path
,
'.dart_tool'
,
'build'
,
'flutter_web'
,
));
if
(
assetGraphFile
.
existsSync
())
{
assetGraphFile
.
deleteSync
();
}
if
(
cacheDirectory
.
existsSync
())
{
cacheDirectory
.
deleteSync
(
recursive:
true
);
}
}
Future
<
void
>
_cleanUpSourceOutputs
(
AssetGraph
assetGraph
,
core
.
PackageGraph
packageGraph
)
async
{
final
core
.
FileBasedAssetWriter
writer
=
core
.
FileBasedAssetWriter
(
packageGraph
);
if
(
assetGraph
?.
outputs
==
null
)
{
return
;
}
for
(
AssetId
id
in
assetGraph
.
outputs
)
{
if
(
id
.
package
!=
packageGraph
.
root
.
name
)
{
continue
;
}
final
GeneratedAssetNode
node
=
assetGraph
.
get
(
id
);
if
(
node
.
wasOutput
)
{
// Note that this does a file.exists check in the root package and
// only tries to delete the file if it exists. This way we only
// actually delete to_source outputs, without reading in the build
// actions.
await
writer
.
delete
(
id
);
}
}
}
}
/// A ddc-only entrypoint builder that respects the Flutter target flag.
class
FlutterWebEntrypointBuilder
implements
Builder
{
const
FlutterWebEntrypointBuilder
(
this
.
targets
);
const
FlutterWebEntrypointBuilder
(
this
.
targets
,
this
.
release
);
final
List
<
String
>
targets
;
final
bool
release
;
@override
Map
<
String
,
List
<
String
>>
get
buildExtensions
=>
const
<
String
,
List
<
String
>>{
...
...
@@ -254,7 +369,7 @@ class FlutterWebEntrypointBuilder implements Builder {
Future
<
void
>
build
(
BuildStep
buildStep
)
async
{
bool
matches
=
false
;
for
(
String
target
in
targets
)
{
if
(
buildStep
.
inputId
.
path
.
contains
(
target
))
{
if
(
buildStep
.
inputId
.
path
.
contains
(
fs
.
path
.
setExtension
(
target
,
'_web_entrypoint.dart'
)
))
{
matches
=
true
;
break
;
}
...
...
@@ -263,10 +378,15 @@ class FlutterWebEntrypointBuilder implements Builder {
return
;
}
log
.
info
(
'building for target
${buildStep.inputId.path}
'
);
if
(
release
)
{
await
bootstrapDart2Js
(
buildStep
);
}
else
{
await
bootstrapDdc
(
buildStep
,
platform:
flutterWebPlatform
);
}
}
}
/// Bootstraps the test entrypoint.
class
FlutterWebTestBootstrapBuilder
implements
Builder
{
const
FlutterWebTestBootstrapBuilder
();
...
...
@@ -372,3 +492,117 @@ void setStackTraceMapper(StackTraceMapper mapper) {
}
}
/// A shell builder which generates the web specific entrypoint.
class FlutterWebShellBuilder implements Builder {
const FlutterWebShellBuilder(this.targets);
final List<String> targets;
@override
FutureOr<void> build(BuildStep buildStep) async {
bool matches = false;
for (String target in targets) {
if (buildStep.inputId.path.contains(target)) {
matches = true;
break;
}
}
if (!matches) {
return;
}
final AssetId outputId = buildStep.inputId.changeExtension('
_web_entrypoint
.
dart
');
await buildStep.writeAsString(outputId, '''
import
'dart:ui'
as
ui
;
import
"
${path.url.basename(buildStep.inputId.path)}
"
as
entrypoint
;
Future
<
void
>
main
()
async
{
await
ui
.
webOnlyInitializePlatform
();
entrypoint
.
main
();
}
''');
}
@override
Map<String, List<String>> get buildExtensions => const <String, List<String>>{
'
.
dart
': <String>['
_web_entrypoint
.
dart
'],
};
}
Future<void> bootstrapDart2Js(BuildStep buildStep) async {
final AssetId dartEntrypointId = buildStep.inputId;
final AssetId moduleId = dartEntrypointId.changeExtension(moduleExtension(flutterWebPlatform));
final Module module = Module.fromJson(json.decode(await buildStep.readAsString(moduleId)));
final List<Module> allDeps = await module.computeTransitiveDependencies(buildStep, throwIfUnsupported: false)..add(module);
final ScratchSpace scratchSpace = await buildStep.fetchResource(scratchSpaceResource);
final Iterable<AssetId> allSrcs = allDeps.expand((Module module) => module.sources);
await scratchSpace.ensureAssets(allSrcs, buildStep);
final String packageFile = await _createPackageFile(allSrcs, buildStep, scratchSpace);
final String dartPath = dartEntrypointId.path.startsWith('
lib
/
')
? '
package:
$
{
dartEntrypointId
.
package
}/
'
'
$
{
dartEntrypointId
.
path
.
substring
(
'lib/'
.
length
)}
'
: dartEntrypointId.path;
final String jsOutputPath =
'
$
{
fs
.
path
.
withoutExtension
(
dartPath
.
replaceFirst
(
'package:'
,
'packages/'
))}
'
'
$jsEntrypointExtension
';
final String flutterWebSdkPath = artifacts.getArtifactPath(Artifact.flutterWebSdk);
final String librariesPath = fs.path.join(flutterWebSdkPath, '
libraries
.
json
');
final List<String> args = <String>[
'
--
libraries
-
spec
=
"
$librariesPath
"',
'
-
m
',
'
-
o4
',
'
-
o
',
'
$jsOutputPath
',
'
--
packages
=
"
$packageFile
"',
dartPath,
];
final Dart2JsBatchWorkerPool dart2js = await buildStep.fetchResource(dart2JsWorkerResource);
final Dart2JsResult result = await dart2js.compile(args);
final AssetId jsOutputId = dartEntrypointId.changeExtension(jsEntrypointExtension);
final io.File jsOutputFile = scratchSpace.fileFor(jsOutputId);
if (result.succeeded && jsOutputFile.existsSync()) {
log.info(result.output);
// Explicitly write out the original js file and sourcemap.
await scratchSpace.copyOutput(jsOutputId, buildStep);
final AssetId jsSourceMapId =
dartEntrypointId.changeExtension(jsEntrypointSourceMapExtension);
await _copyIfExists(jsSourceMapId, scratchSpace, buildStep);
} else {
log.severe(result.output);
}
}
Future<void> _copyIfExists(
AssetId id, ScratchSpace scratchSpace, AssetWriter writer) async {
final io.File file = scratchSpace.fileFor(id);
if (file.existsSync()) {
await scratchSpace.copyOutput(id, writer);
}
}
/// Creates a `.packages` file unique to this entrypoint at the root of the
/// scratch space and returns it'
s
filename
.
///
/// Since mulitple invocations of Dart2Js will share a scratch space and we only
/// know the set of packages involved the current entrypoint we can't construct
/// a `.packages` file that will work for all invocations of Dart2Js so a unique
/// file is created for every entrypoint that is run.
///
/// The filename is based off the MD5 hash of the asset path so that files are
/// unique regarless of situations like `web/foo/bar.dart` vs
/// `web/foo-bar.dart`.
Future
<
String
>
_createPackageFile
(
Iterable
<
AssetId
>
inputSources
,
BuildStep
buildStep
,
ScratchSpace
scratchSpace
)
async
{
final
Uri
inputUri
=
buildStep
.
inputId
.
uri
;
final
String
packageFileName
=
'.package-
${md5.convert(inputUri.toString().codeUnits)}
'
;
final
io
.
File
packagesFile
=
scratchSpace
.
fileFor
(
AssetId
(
buildStep
.
inputId
.
package
,
packageFileName
));
final
Set
<
String
>
packageNames
=
inputSources
.
map
((
AssetId
s
)
=>
s
.
package
).
toSet
();
final
String
packagesFileContent
=
packageNames
.
map
((
String
name
)
=>
'
$name
:packages/
$name
/'
).
join
(
'
\n
'
);
await
packagesFile
.
writeAsString
(
'# Generated for
$inputUri
\n
$packagesFileContent
'
);
return
packageFileName
;
}
packages/flutter_tools/lib/src/commands/build_web.dart
View file @
c91b6571
...
...
@@ -4,10 +4,8 @@
import
'dart:async'
;
import
'../base/common.dart'
;
import
'../base/logger.dart'
;
import
'../build_info.dart'
;
import
'../
globals
.dart'
;
import
'../
project
.dart'
;
import
'../runner/flutter_command.dart'
show
DevelopmentArtifact
,
FlutterCommandResult
;
import
'../web/compile.dart'
;
...
...
@@ -41,34 +39,10 @@ class BuildWebCommand extends BuildSubCommand {
@override
Future
<
FlutterCommandResult
>
runCommand
()
async
{
final
FlutterProject
flutterProject
=
FlutterProject
.
current
();
final
String
target
=
argResults
[
'target'
];
final
Status
status
=
logger
.
startProgress
(
'Compiling
$target
for the Web...'
,
timeout:
null
);
final
BuildInfo
buildInfo
=
getBuildInfo
();
int
result
;
switch
(
buildInfo
.
mode
)
{
case
BuildMode
.
release
:
result
=
await
webCompiler
.
compileDart2js
(
target:
target
);
break
;
case
BuildMode
.
profile
:
result
=
await
webCompiler
.
compileDart2js
(
target:
target
,
minify:
false
);
break
;
case
BuildMode
.
debug
:
throwToolExit
(
'Debug mode is not supported as a build target. Instead use '
'"flutter run -d web".'
);
break
;
case
BuildMode
.
dynamicProfile
:
case
BuildMode
.
dynamicRelease
:
throwToolExit
(
'Build mode
${buildInfo.mode}
is not supported with JavaScript '
'compilation'
);
break
;
}
status
.
stop
();
if
(
result
==
1
)
{
throwToolExit
(
'Failed to compile
$target
to JavaScript.'
);
}
await
buildWeb
(
flutterProject
,
target
,
buildInfo
);
return
null
;
}
}
packages/flutter_tools/lib/src/context_runner.dart
View file @
c91b6571
...
...
@@ -44,7 +44,6 @@ import 'run_hot.dart';
import
'usage.dart'
;
import
'version.dart'
;
import
'web/chrome.dart'
;
import
'web/compile.dart'
;
import
'web/workflow.dart'
;
import
'windows/visual_studio.dart'
;
import
'windows/visual_studio_validator.dart'
;
...
...
@@ -104,7 +103,6 @@ Future<T> runInContext<T>(
UserMessages:
()
=>
UserMessages
(),
VisualStudio:
()
=>
VisualStudio
(),
VisualStudioValidator:
()
=>
const
VisualStudioValidator
(),
WebCompiler:
()
=>
const
WebCompiler
(),
WebWorkflow:
()
=>
const
WebWorkflow
(),
WindowsWorkflow:
()
=>
const
WindowsWorkflow
(),
Xcode:
()
=>
Xcode
(),
...
...
packages/flutter_tools/lib/src/project.dart
View file @
c91b6571
...
...
@@ -583,6 +583,9 @@ class WebProject {
return
parent
.
directory
.
childDirectory
(
'web'
).
existsSync
();
}
/// The html file used to host the flutter web application.
File
get
indexFile
=>
parent
.
directory
.
childDirectory
(
'web'
).
childFile
(
'index.html'
);
Future
<
void
>
ensureReadyForPlatformSpecificTooling
()
async
{
/// Generate index.html in build/web. Eventually we could support
/// a custom html under the web sub directory.
...
...
packages/flutter_tools/lib/src/web/asset_server.dart
View file @
c91b6571
...
...
@@ -66,6 +66,7 @@ class WebAssetServer {
/// An HTTP server which provides JavaScript and web assets to the browser.
Future
<
void
>
_onRequest
(
HttpRequest
request
)
async
{
final
String
targetName
=
'
${fs.path.basenameWithoutExtension(target)}
_web_entrypoint'
;
if
(
request
.
method
!=
'GET'
)
{
request
.
response
.
statusCode
=
HttpStatus
.
forbidden
;
await
request
.
response
.
close
();
...
...
@@ -103,17 +104,17 @@ class WebAssetServer {
'flutter_web'
,
flutterProject
.
manifest
.
appName
,
'lib'
,
'
$
{fs.path.basename(target)}
.js'
,
'
$
targetName
.dart
.js'
,
));
await
_completeRequest
(
request
,
file
,
'text/javascript'
);
}
else
if
(
uri
.
path
.
endsWith
(
'
$
{fs.path.basename(target)}
.bootstrap.js'
))
{
}
else
if
(
uri
.
path
.
endsWith
(
'
$
targetName
.dart
.bootstrap.js'
))
{
final
File
file
=
fs
.
file
(
fs
.
path
.
join
(
flutterProject
.
dartTool
.
path
,
'build'
,
'flutter_web'
,
flutterProject
.
manifest
.
appName
,
'lib'
,
'
$
{fs.path.basename(target)}
.bootstrap.js'
,
'
$
targetName
.dart
.bootstrap.js'
,
));
await
_completeRequest
(
request
,
file
,
'text/javascript'
);
}
else
if
(
uri
.
path
.
contains
(
'dart_sdk'
))
{
...
...
packages/flutter_tools/lib/src/web/compile.dart
View file @
c91b6571
...
...
@@ -4,80 +4,56 @@
import
'package:meta/meta.dart'
;
import
'../a
rtifacts
.dart'
;
import
'../a
sset
.dart'
;
import
'../base/common.dart'
;
import
'../base/context.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/process_manager.dart'
;
import
'../base/logger.dart'
;
import
'../build_info.dart'
;
import
'../
convert
.dart'
;
import
'../
bundle
.dart'
;
import
'../globals.dart'
;
/// The [WebCompiler] instance.
WebCompiler
get
webCompiler
=>
context
.
get
<
WebCompiler
>();
import
'../project.dart'
;
/// The [WebCompilationProxy] instance.
WebCompilationProxy
get
webCompilationProxy
=>
context
.
get
<
WebCompilationProxy
>();
WebCompilationProxy
get
webCompilationProxy
=>
context
.
get
<
WebCompilationProxy
>();
/// A wrapper around dart tools for web compilation.
class
WebCompiler
{
const
WebCompiler
();
Future
<
void
>
buildWeb
(
FlutterProject
flutterProject
,
String
target
,
BuildInfo
buildInfo
)
async
{
final
Status
status
=
logger
.
startProgress
(
'Compiling
$target
for the Web...'
,
timeout:
null
);
final
Directory
outputDir
=
fs
.
directory
(
getWebBuildDirectory
())
..
createSync
(
recursive:
true
);
bool
result
;
try
{
result
=
await
webCompilationProxy
.
initialize
(
projectDirectory:
FlutterProject
.
current
().
directory
,
targets:
<
String
>[
target
],
release:
buildInfo
.
isRelease
,
);
if
(
result
)
{
// Places assets adjacent to the web stuff.
final
AssetBundle
assetBundle
=
AssetBundleFactory
.
instance
.
createBundle
();
await
assetBundle
.
build
();
await
writeBundle
(
fs
.
directory
(
fs
.
path
.
join
(
outputDir
.
path
,
'assets'
)),
assetBundle
.
entries
);
/// Compile `target` using dart2js.
///
/// `minify` controls whether minifaction of the source is enabled. Defaults to `true`.
/// `enabledAssertions` controls whether assertions are enabled. Defaults to `false`.
Future
<
int
>
compileDart2js
({
@required
String
target
,
bool
minify
=
true
,
bool
enabledAssertions
=
false
,
})
async
{
final
String
engineDartPath
=
artifacts
.
getArtifactPath
(
Artifact
.
engineDartBinary
);
final
String
dart2jsPath
=
artifacts
.
getArtifactPath
(
Artifact
.
dart2jsSnapshot
);
final
String
flutterWebSdkPath
=
artifacts
.
getArtifactPath
(
Artifact
.
flutterWebSdk
);
final
String
librariesPath
=
fs
.
path
.
join
(
flutterWebSdkPath
,
'libraries.json'
);
final
Directory
outputDir
=
fs
.
directory
(
getWebBuildDirectory
());
if
(!
outputDir
.
existsSync
())
{
outputDir
.
createSync
(
recursive:
true
);
// Copy results to output directory.
final
String
outputPath
=
fs
.
path
.
join
(
flutterProject
.
dartTool
.
path
,
'build'
,
'flutter_web'
,
flutterProject
.
manifest
.
appName
,
'
${fs.path.withoutExtension(target)}
_web_entrypoint.dart.js'
);
fs
.
file
(
outputPath
).
copySync
(
fs
.
path
.
join
(
outputDir
.
path
,
'main.dart.js'
));
fs
.
file
(
'
$outputPath
.map'
).
copySync
(
fs
.
path
.
join
(
outputDir
.
path
,
'main.dart.js.map'
));
flutterProject
.
web
.
indexFile
.
copySync
(
fs
.
path
.
join
(
outputDir
.
path
,
'index.html'
));
}
final
String
outputPath
=
fs
.
path
.
join
(
outputDir
.
path
,
'main.dart.js'
);
if
(!
processManager
.
canRun
(
engineDartPath
))
{
throwToolExit
(
'Unable to find Dart binary at
$engineDartPath
'
);
}
/// Compile Dart to JavaScript.
final
List
<
String
>
command
=
<
String
>[
engineDartPath
,
dart2jsPath
,
target
,
'-o'
,
'
$outputPath
'
,
'-O4'
,
'--libraries-spec=
$librariesPath
'
,
];
if
(
minify
)
{
command
.
add
(
'-m'
);
}
catch
(
err
)
{
printError
(
err
.
toString
());
result
=
false
;
}
finally
{
status
.
stop
();
}
if
(
enabledAssertions
)
{
command
.
add
(
'--enable-asserts'
);
}
printTrace
(
command
.
join
(
' '
));
final
Process
result
=
await
processManager
.
start
(
command
);
result
.
stdout
.
transform
(
utf8
.
decoder
)
.
transform
(
const
LineSplitter
())
.
listen
(
printStatus
);
result
.
stderr
.
transform
(
utf8
.
decoder
)
.
transform
(
const
LineSplitter
())
.
listen
(
printError
);
return
result
.
exitCode
;
if
(
result
==
false
)
{
throwToolExit
(
'Failed to compile
$target
for the Web.'
);
}
}
...
...
@@ -87,12 +63,19 @@ class WebCompiler {
class
WebCompilationProxy
{
const
WebCompilationProxy
();
/// Initialize the web compiler output to `outputDirectory` from a project spawned at
/// `projectDirectory`.
Future
<
void
>
initialize
({
/// Initialize the web compiler from the `projectDirectory`.
///
/// Returns whether or not the build was successful.
///
/// `release` controls whether we build the bundle for dartdevc or only
/// the entrypoints for dart2js to later take over.
///
/// `targets` controls the specific compiler targets.
Future
<
bool
>
initialize
({
@required
Directory
projectDirectory
,
@required
List
<
String
>
targets
,
String
testOutputDir
,
bool
release
,
})
async
{
throw
UnimplementedError
();
}
...
...
packages/flutter_tools/lib/src/web/web_device.dart
View file @
c91b6571
...
...
@@ -5,15 +5,11 @@
import
'package:meta/meta.dart'
;
import
'../application_package.dart'
;
import
'../asset.dart'
;
import
'../base/common.dart'
;
import
'../base/file_system.dart'
;
import
'../base/io.dart'
;
import
'../base/logger.dart'
;
import
'../base/platform.dart'
;
import
'../base/process_manager.dart'
;
import
'../build_info.dart'
;
import
'../bundle.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../project.dart'
;
...
...
@@ -22,15 +18,15 @@ import '../web/workflow.dart';
import
'chrome.dart'
;
class
WebApplicationPackage
extends
ApplicationPackage
{
WebApplicationPackage
(
this
.
_flutterProject
)
:
super
(
id:
_
flutterProject
.
manifest
.
appName
);
WebApplicationPackage
(
this
.
flutterProject
)
:
super
(
id:
flutterProject
.
manifest
.
appName
);
final
FlutterProject
_
flutterProject
;
final
FlutterProject
flutterProject
;
@override
String
get
name
=>
_
flutterProject
.
manifest
.
appName
;
String
get
name
=>
flutterProject
.
manifest
.
appName
;
/// The location of the web source assets.
Directory
get
webSourcePath
=>
_
flutterProject
.
directory
.
childDirectory
(
'web'
);
Directory
get
webSourcePath
=>
flutterProject
.
directory
.
childDirectory
(
'web'
);
}
class
WebDevice
extends
Device
{
...
...
@@ -121,20 +117,11 @@ class WebDevice extends Device {
bool
usesTerminalUi
=
true
,
bool
ipv6
=
false
,
})
async
{
final
Status
status
=
logger
.
startProgress
(
'Compiling
${package.name}
to JavaScript...'
,
timeout:
null
);
final
int
result
=
await
webCompiler
.
compileDart2js
(
target:
mainPath
,
minify:
false
,
enabledAssertions:
true
);
status
.
stop
();
if
(
result
!=
0
)
{
printError
(
'Failed to compile
${package.name}
to JavaScript'
);
return
LaunchResult
.
failed
();
}
final
AssetBundle
assetBundle
=
AssetBundleFactory
.
instance
.
createBundle
();
final
int
build
=
await
assetBundle
.
build
();
if
(
build
!=
0
)
{
throwToolExit
(
'Error: Failed to build asset bundle'
);
}
await
writeBundle
(
fs
.
directory
(
getAssetBuildDirectory
()),
assetBundle
.
entries
);
await
buildWeb
(
package
.
flutterProject
,
fs
.
path
.
relative
(
mainPath
,
from:
package
.
flutterProject
.
directory
.
path
),
debuggingOptions
.
buildInfo
,
);
_package
=
package
;
_server
=
await
HttpServer
.
bind
(
InternetAddress
.
loopbackIPv4
,
0
);
_server
.
listen
(
_basicAssetServer
);
...
...
packages/flutter_tools/test/web/compile_test.dart
deleted
100644 → 0
View file @
4e5cf5ef
// Copyright 2019 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
'package:flutter_tools/src/artifacts.dart'
;
import
'package:flutter_tools/src/globals.dart'
;
import
'package:flutter_tools/src/web/compile.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:process/process.dart'
;
import
'../src/common.dart'
;
import
'../src/mocks.dart'
;
import
'../src/testbed.dart'
;
void
main
(
)
{
group
(
WebCompiler
,
()
{
MockProcessManager
mockProcessManager
;
Testbed
testBed
;
setUp
(()
{
mockProcessManager
=
MockProcessManager
();
testBed
=
Testbed
(
setup:
()
async
{
final
String
engineDartPath
=
artifacts
.
getArtifactPath
(
Artifact
.
engineDartBinary
);
when
(
mockProcessManager
.
start
(
any
)).
thenAnswer
((
Invocation
invocation
)
async
=>
FakeProcess
());
when
(
mockProcessManager
.
canRun
(
engineDartPath
)).
thenReturn
(
true
);
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
mockProcessManager
,
});
});
test
(
'invokes dart2js with correct arguments'
,
()
=>
testBed
.
run
(()
async
{
await
webCompiler
.
compileDart2js
(
target:
'lib/main.dart'
);
verify
(
mockProcessManager
.
start
(<
String
>[
'bin/cache/dart-sdk/bin/dart'
,
'bin/cache/dart-sdk/bin/snapshots/dart2js.dart.snapshot'
,
'lib/main.dart'
,
'-o'
,
'build/web/main.dart.js'
,
'-O4'
,
'--libraries-spec=bin/cache/flutter_web_sdk/libraries.json'
,
'-m'
,
])).
called
(
1
);
}));
});
}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
packages/flutter_tools/test/web/devices_test.dart
View file @
c91b6571
...
...
@@ -2,12 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:flutter_tools/src/web/chrome.dart'
;
import
'package:flutter_tools/src/web/compile.dart'
;
import
'package:flutter_tools/src/web/web_device.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:process/process.dart'
;
...
...
@@ -17,37 +14,18 @@ import '../src/context.dart';
void
main
(
)
{
group
(
WebDevice
,
()
{
MockWebCompiler
mockWebCompiler
;
MockChromeLauncher
mockChromeLauncher
;
MockPlatform
mockPlatform
;
FlutterProject
flutterProject
;
MockProcessManager
mockProcessManager
;
setUp
(()
async
{
mockProcessManager
=
MockProcessManager
();
mockChromeLauncher
=
MockChromeLauncher
();
mockPlatform
=
MockPlatform
();
mockWebCompiler
=
MockWebCompiler
();
flutterProject
=
FlutterProject
.
fromPath
(
fs
.
path
.
join
(
getFlutterRoot
(),
'dev'
,
'integration_tests'
,
'web'
));
when
(
mockWebCompiler
.
compileDart2js
(
target:
anyNamed
(
'target'
),
minify:
anyNamed
(
'minify'
),
enabledAssertions:
anyNamed
(
'enabledAssertions'
),
)).
thenAnswer
((
Invocation
invocation
)
async
=>
0
);
when
(
mockChromeLauncher
.
launch
(
any
)).
thenAnswer
((
Invocation
invocation
)
async
{
return
null
;
});
});
testUsingContext
(
'can build and connect to chrome'
,
()
async
{
final
WebDevice
device
=
WebDevice
();
await
device
.
startApp
(
WebApplicationPackage
(
flutterProject
));
},
overrides:
<
Type
,
Generator
>{
ChromeLauncher:
()
=>
mockChromeLauncher
,
WebCompiler:
()
=>
mockWebCompiler
,
Platform:
()
=>
mockPlatform
,
});
testUsingContext
(
'Invokes version command on non-Windows platforms'
,
()
async
{
when
(
mockPlatform
.
isWindows
).
thenReturn
(
false
);
when
(
mockPlatform
.
environment
).
thenReturn
(<
String
,
String
>{
...
...
@@ -86,7 +64,6 @@ void main() {
}
class
MockChromeLauncher
extends
Mock
implements
ChromeLauncher
{}
class
MockWebCompiler
extends
Mock
implements
WebCompiler
{}
class
MockPlatform
extends
Mock
implements
Platform
{}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
class
MockProcessResult
extends
Mock
implements
ProcessResult
{
...
...
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