diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index a8aadef1a9ff5..3c1174f16138d 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -768,24 +768,22 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts { @override - FileSystemEntity getHostArtifact( - HostArtifact artifact, - ) { + FileSystemEntity getHostArtifact(HostArtifact artifact) { switch (artifact) { case HostArtifact.engineDartSdkPath: - final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk'); + final String path = _getDartSdkPath(); return _fileSystem.directory(path); case HostArtifact.engineDartBinary: - final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', _hostArtifactToFileName(artifact, _platform)); + final String path = _fileSystem.path.join(_getDartSdkPath(), 'bin', _hostArtifactToFileName(artifact, _platform)); return _fileSystem.file(path); case HostArtifact.dart2jsSnapshot: - final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform)); + final String path = _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform)); return _fileSystem.file(path); case HostArtifact.dartdevcSnapshot: - final String path = _fileSystem.path.join(_dartSdkPath(_cache), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform)); + final String path = _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform)); return _fileSystem.file(path); case HostArtifact.kernelWorkerSnapshot: - final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform)); + final String path = _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform)); return _fileSystem.file(path); case HostArtifact.flutterWebSdk: final String path = _getFlutterWebSdkPath(); @@ -908,7 +906,7 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts { return _fileSystem.path.join(_hostEngineOutPath, artifactFileName); case Artifact.frontendServerSnapshotForEngineDartSdk: return _fileSystem.path.join( - _hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName, + _getDartSdkPath(), 'bin', 'snapshots', artifactFileName, ); } } @@ -923,6 +921,51 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts { buildMode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk'); } + String _getDartSdkPath() { + final String builtPath = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk'); + if (_fileSystem.isDirectorySync(_fileSystem.path.join(builtPath, 'bin'))) { + return builtPath; + } + + // If we couldn't find a built dart sdk, let's look for a prebuilt one. + final String prebuiltPath = _fileSystem.path.join(_getFlutterPrebuiltsPath(), _getPrebuiltTarget(), 'dart-sdk'); + if (_fileSystem.isDirectorySync(prebuiltPath)) { + return prebuiltPath; + } + + throw ToolExit('Unable to find a built dart sdk at: "$builtPath" or a prebuilt dart sdk at: "$prebuiltPath"'); + } + + String _getFlutterPrebuiltsPath() { + final String engineSrcPath = _fileSystem.path.dirname(_fileSystem.path.dirname(_hostEngineOutPath)); + return _fileSystem.path.join(engineSrcPath, 'flutter', 'prebuilts'); + } + + String _getPrebuiltTarget() { + final TargetPlatform hostPlatform = _currentHostPlatform(_platform, _operatingSystemUtils); + switch (hostPlatform) { + case TargetPlatform.darwin: + return 'macos-x64'; + case TargetPlatform.linux_arm64: + return 'linux-arm64'; + case TargetPlatform.linux_x64: + return 'linux-x64'; + case TargetPlatform.windows_x64: + return 'windows-x64'; + case TargetPlatform.ios: + case TargetPlatform.android: + case TargetPlatform.android_arm: + case TargetPlatform.android_arm64: + case TargetPlatform.android_x64: + case TargetPlatform.android_x86: + case TargetPlatform.fuchsia_arm64: + case TargetPlatform.fuchsia_x64: + case TargetPlatform.web_javascript: + case TargetPlatform.tester: + throwToolExit('Unsupported host platform: $hostPlatform'); + } + } + String _getFlutterWebSdkPath() { return _fileSystem.path.join(engineOutPath, 'flutter_web_sdk'); } diff --git a/packages/flutter_tools/lib/src/asset.dart b/packages/flutter_tools/lib/src/asset.dart index 7d851d8ee927b..8fb68115d210f 100644 --- a/packages/flutter_tools/lib/src/asset.dart +++ b/packages/flutter_tools/lib/src/asset.dart @@ -252,6 +252,7 @@ class ManifestAssetBundle implements AssetBundle { flutterManifest, wildcardDirectories, assetBasePath, + targetPlatform, ); if (assetVariants == null) { @@ -316,6 +317,7 @@ class ManifestAssetBundle implements AssetBundle { // Do not track wildcard directories for dependencies. [], packageBasePath, + targetPlatform, packageName: package.name, attributedPackage: package, ); @@ -407,10 +409,11 @@ class ManifestAssetBundle implements AssetBundle { final List<_Asset> materialAssets = <_Asset>[ if (flutterManifest.usesMaterialDesign) ..._getMaterialFonts(), - // Include the shaders unconditionally. They are small, and whether - // they're used is determined only by the app source code and not by - // the Flutter manifest. - ..._getMaterialShaders(), + // For non-web platforms, include the shaders unconditionally. They are + // small, and whether they're used is determined only by the app source + // code and not by the Flutter manifest. + if (targetPlatform != TargetPlatform.web_javascript) + ..._getMaterialShaders(), ]; for (final _Asset asset in materialAssets) { final File assetFile = asset.lookupAssetFile(_fileSystem); @@ -716,7 +719,8 @@ class ManifestAssetBundle implements AssetBundle { PackageConfig packageConfig, FlutterManifest flutterManifest, List wildcardDirectories, - String assetBase, { + String assetBase, + TargetPlatform? targetPlatform, { String? packageName, Package? attributedPackage, }) { @@ -750,18 +754,21 @@ class ManifestAssetBundle implements AssetBundle { } } - for (final Uri shaderUri in flutterManifest.shaders) { - _parseAssetFromFile( - packageConfig, - flutterManifest, - assetBase, - cache, - result, - shaderUri, - packageName: packageName, - attributedPackage: attributedPackage, - assetKind: AssetKind.shader, - ); + // No shader compilation for the web. + if (targetPlatform != TargetPlatform.web_javascript) { + for (final Uri shaderUri in flutterManifest.shaders) { + _parseAssetFromFile( + packageConfig, + flutterManifest, + assetBase, + cache, + result, + shaderUri, + packageName: packageName, + attributedPackage: attributedPackage, + assetKind: AssetKind.shader, + ); + } } // Add assets referenced in the fonts section of the manifest. diff --git a/packages/flutter_tools/lib/src/runner/local_engine.dart b/packages/flutter_tools/lib/src/runner/local_engine.dart index e0fcb819a8218..50537146d28e2 100644 --- a/packages/flutter_tools/lib/src/runner/local_engine.dart +++ b/packages/flutter_tools/lib/src/runner/local_engine.dart @@ -150,6 +150,11 @@ class LocalEngineLocator { // Determine the host engine directory associated with the local engine: // Strip '_sim_' since there are no host simulator builds. String _getHostEngineBasename(String localEngineBasename) { + if (localEngineBasename.startsWith('web_') || localEngineBasename.startsWith('wasm_')) { + // Don't modify the web local engine's basename. + return localEngineBasename; + } + String tmpBasename = localEngineBasename.replaceFirst('_sim_', '_'); tmpBasename = tmpBasename.substring(tmpBasename.indexOf('_') + 1); // Strip suffix for various archs. diff --git a/packages/flutter_tools/test/general.shard/artifacts_test.dart b/packages/flutter_tools/test/general.shard/artifacts_test.dart index aed3eb9285104..2e00ce8646887 100644 --- a/packages/flutter_tools/test/general.shard/artifacts_test.dart +++ b/packages/flutter_tools/test/general.shard/artifacts_test.dart @@ -273,6 +273,13 @@ void main() { .childDirectory('ios-arm64_armv7') .childDirectory('Flutter.framework') .createSync(recursive: true); + fileSystem + .directory('out') + .childDirectory('host_debug_unopt') + .childDirectory('dart-sdk') + .childDirectory('bin') + .createSync(recursive: true); + expect( artifacts.getArtifactPath( Artifact.flutterFramework, @@ -325,6 +332,75 @@ void main() { ); }); + testWithoutContext('falls back to prebuilt dart sdk', () { + final String failureMessage = 'Unable to find a built dart sdk at:' + ' "${fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk')}"' + ' or a prebuilt dart sdk at:' + ' "${fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk')}"'; + + expect( + () => artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk), + throwsToolExit(message: failureMessage), + ); + expect( + () => artifacts.getHostArtifact(HostArtifact.engineDartSdkPath), + throwsToolExit(message: failureMessage), + ); + expect( + () => artifacts.getHostArtifact(HostArtifact.engineDartBinary), + throwsToolExit(message: failureMessage), + ); + expect( + () => artifacts.getHostArtifact(HostArtifact.dart2jsSnapshot), + throwsToolExit(message: failureMessage), + ); + expect( + () => artifacts.getHostArtifact(HostArtifact.dartdevcSnapshot), + throwsToolExit(message: failureMessage), + ); + expect( + () => artifacts.getHostArtifact(HostArtifact.kernelWorkerSnapshot), + throwsToolExit(message: failureMessage), + ); + + fileSystem + .directory('flutter') + .childDirectory('prebuilts') + .childDirectory('linux-x64') + .childDirectory('dart-sdk') + .childDirectory('bin') + .createSync(recursive: true); + + expect( + artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk), + fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk', 'bin', + 'snapshots', 'frontend_server.dart.snapshot'), + ); + expect( + artifacts.getHostArtifact(HostArtifact.engineDartSdkPath).path, + fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk'), + ); + expect( + artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, + fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk', 'bin', 'dart'), + ); + expect( + artifacts.getHostArtifact(HostArtifact.dart2jsSnapshot).path, + fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk', + 'bin', 'snapshots', 'dart2js.dart.snapshot'), + ); + expect( + artifacts.getHostArtifact(HostArtifact.dartdevcSnapshot).path, + fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk', + 'bin', 'snapshots', 'dartdevc.dart.snapshot'), + ); + expect( + artifacts.getHostArtifact(HostArtifact.kernelWorkerSnapshot).path, + fileSystem.path.join('/flutter', 'prebuilts', 'linux-x64', 'dart-sdk', + 'bin', 'snapshots', 'kernel_worker.dart.snapshot'), + ); + }); + testWithoutContext('getEngineType', () { expect( artifacts.getEngineType(TargetPlatform.android_arm, BuildMode.debug), @@ -351,11 +427,81 @@ void main() { operatingSystemUtils: FakeOperatingSystemUtils(), ); - expect(artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, contains('.exe')); + fileSystem + .directory('out') + .childDirectory('host_debug_unopt') + .childDirectory('dart-sdk') + .childDirectory('bin') + .createSync(recursive: true); + + expect( + artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, + fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk', 'bin', 'dart.exe'), + ); }); testWithoutContext('Looks up dart on linux platforms', () async { - expect(artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, isNot(contains('.exe'))); + fileSystem + .directory('/out') + .childDirectory('host_debug_unopt') + .childDirectory('dart-sdk') + .childDirectory('bin') + .createSync(recursive: true); + + expect( + artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, + fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk', 'bin', 'dart'), + ); + }); + + testWithoutContext('Finds dart-sdk in windows prebuilts', () async { + artifacts = LocalEngineArtifacts( + fileSystem.path.join(fileSystem.currentDirectory.path, 'out', 'android_debug_unopt'), + fileSystem.path.join(fileSystem.currentDirectory.path, 'out', 'host_debug_unopt'), + cache: cache, + fileSystem: fileSystem, + platform: FakePlatform(operatingSystem: 'windows'), + processManager: FakeProcessManager.any(), + operatingSystemUtils: FakeOperatingSystemUtils(), + ); + + fileSystem + .directory('/flutter') + .childDirectory('prebuilts') + .childDirectory('windows-x64') + .childDirectory('dart-sdk') + .childDirectory('bin') + .createSync(recursive: true); + + expect( + artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, + fileSystem.path.join('/flutter', 'prebuilts', 'windows-x64', 'dart-sdk', 'bin', 'dart.exe'), + ); + }); + + testWithoutContext('Finds dart-sdk in macos prebuilts', () async { + artifacts = LocalEngineArtifacts( + fileSystem.path.join(fileSystem.currentDirectory.path, 'out', 'android_debug_unopt'), + fileSystem.path.join(fileSystem.currentDirectory.path, 'out', 'host_debug_unopt'), + cache: cache, + fileSystem: fileSystem, + platform: FakePlatform(operatingSystem: 'macos'), + processManager: FakeProcessManager.any(), + operatingSystemUtils: FakeOperatingSystemUtils(), + ); + + fileSystem + .directory('/flutter') + .childDirectory('prebuilts') + .childDirectory('macos-x64') + .childDirectory('dart-sdk') + .childDirectory('bin') + .createSync(recursive: true); + + expect( + artifacts.getHostArtifact(HostArtifact.engineDartBinary).path, + fileSystem.path.join('/flutter', 'prebuilts', 'macos-x64', 'dart-sdk', 'bin', 'dart'), + ); }); }); } diff --git a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart index ff4e3dcba270a..2c6fbaf17f4e1 100644 --- a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart +++ b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart @@ -9,6 +9,7 @@ import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/asset.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/bundle_builder.dart'; import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/globals.dart' as globals; @@ -457,6 +458,82 @@ flutter: ), ]), }); + + testUsingContext('Included shaders are not compiled for the web', () async { + fileSystem.file('.packages').createSync(); + fileSystem.file('pubspec.yaml') + ..createSync() + ..writeAsStringSync(r''' + name: example + flutter: + shaders: + - assets/shader.frag + '''); + final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); + + expect(await bundle.build(packagesPath: '.packages', targetPlatform: TargetPlatform.web_javascript), 0); + + await writeBundle( + output, + bundle.entries, + bundle.entryKinds, + loggerOverride: testLogger, + ); + + }, overrides: { + Artifacts: () => artifacts, + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.list([ + // No impeller commands are expected here because shader compilation is + // not supposed to happen for the web. + ]), + }); + + testUsingContext('Material shaders are not compiled for the web', () async { + fileSystem.file('.packages').createSync(); + + final String materialIconsPath = fileSystem.path.join( + getFlutterRoot(), + 'bin', 'cache', 'artifacts', 'material_fonts', + 'MaterialIcons-Regular.otf', + ); + fileSystem.file(materialIconsPath).createSync(recursive: true); + + final String materialPath = fileSystem.path.join( + getFlutterRoot(), + 'packages', 'flutter', 'lib', 'src', 'material', + ); + final Directory materialDir = fileSystem.directory(materialPath)..createSync(recursive: true); + for (final String shader in kMaterialShaders) { + materialDir.childFile(shader).createSync(recursive: true); + } + + fileSystem.file('pubspec.yaml') + ..createSync() + ..writeAsStringSync(r''' + name: example + flutter: + uses-material-design: true + '''); + final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); + + expect(await bundle.build(packagesPath: '.packages', targetPlatform: TargetPlatform.web_javascript), 0); + + await writeBundle( + output, + bundle.entries, + bundle.entryKinds, + loggerOverride: testLogger, + ); + + }, overrides: { + Artifacts: () => artifacts, + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.list([ + // No impeller commands are expected here because shader compilation is + // not supposed to happen for the web. + ]), + }); }); testUsingContext('Does not insert dummy file into additionalDependencies ' diff --git a/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart b/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart index 55328826dccd6..7105809b3ba2e 100644 --- a/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart @@ -216,6 +216,52 @@ void main() { contains('Unable to detect local Flutter engine src directory'), ); }); + + testWithoutContext('works for local web engine', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final Directory localWasmEngine = fileSystem + .directory('$kArbitraryEngineRoot/src/out/wasm_whatever/') + ..createSync(recursive: true); + final Directory localWebEngine = fileSystem + .directory('$kArbitraryEngineRoot/src/out/web_whatever/') + ..createSync(recursive: true); + + final BufferLogger wasmLogger = BufferLogger.test(); + final LocalEngineLocator localWasmEngineLocator = LocalEngineLocator( + fileSystem: fileSystem, + flutterRoot: 'flutter/flutter', + logger: wasmLogger, + userMessages: UserMessages(), + platform: FakePlatform(environment: {}), + ); + + expect( + await localWasmEngineLocator.findEnginePath(null, localWasmEngine.path, null), + matchesEngineBuildPaths( + hostEngine: '/arbitrary/engine/src/out/wasm_whatever', + targetEngine: '/arbitrary/engine/src/out/wasm_whatever', + ), + ); + expect(wasmLogger.traceText, contains('Local engine source at /arbitrary/engine/src')); + + final BufferLogger webLogger = BufferLogger.test(); + final LocalEngineLocator localWebEngineLocator = LocalEngineLocator( + fileSystem: fileSystem, + flutterRoot: 'flutter/flutter', + logger: webLogger, + userMessages: UserMessages(), + platform: FakePlatform(environment: {}), + ); + + expect( + await localWebEngineLocator.findEnginePath(null, localWebEngine.path, null), + matchesEngineBuildPaths( + hostEngine: '/arbitrary/engine/src/out/web_whatever', + targetEngine: '/arbitrary/engine/src/out/web_whatever', + ), + ); + expect(webLogger.traceText, contains('Local engine source at /arbitrary/engine/src')); + }); } Matcher matchesEngineBuildPaths({