1// Copyright 2017 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import 'package:meta/meta.dart'; 6 7import 'base/context.dart'; 8import 'base/file_system.dart'; 9import 'base/platform.dart'; 10import 'base/process_manager.dart'; 11import 'base/utils.dart'; 12import 'build_info.dart'; 13import 'dart/sdk.dart'; 14import 'globals.dart'; 15 16enum Artifact { 17 /// The tool which compiles a dart kernel file into native code. 18 genSnapshot, 19 /// The flutter tester binary. 20 flutterTester, 21 snapshotDart, 22 flutterFramework, 23 /// The framework directory of the macOS desktop. 24 flutterMacOSFramework, 25 vmSnapshotData, 26 isolateSnapshotData, 27 platformKernelDill, 28 platformLibrariesJson, 29 flutterPatchedSdkPath, 30 frontendServerSnapshotForEngineDartSdk, 31 /// The root directory of the dartk SDK. 32 engineDartSdkPath, 33 /// The dart binary used to execute any of the required snapshots. 34 engineDartBinary, 35 /// The dart snapshot of the dart2js compiler. 36 dart2jsSnapshot, 37 /// The dart snapshot of the dartdev compiler. 38 dartdevcSnapshot, 39 /// The dart snpashot of the kernel worker compiler. 40 kernelWorkerSnapshot, 41 /// The root of the web implementation of the dart SDK. 42 flutterWebSdk, 43 iosDeploy, 44 ideviceinfo, 45 ideviceId, 46 idevicename, 47 idevicesyslog, 48 idevicescreenshot, 49 ideviceinstaller, 50 iproxy, 51 /// The root of the Linux desktop sources. 52 linuxDesktopPath, 53 /// The root of the Windows desktop sources. 54 windowsDesktopPath, 55 /// The root of the sky_engine package 56 skyEnginePath, 57 /// The location of the macOS engine podspec file. 58 flutterMacOSPodspec, 59} 60 61String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMode mode ]) { 62 switch (artifact) { 63 case Artifact.genSnapshot: 64 return 'gen_snapshot'; 65 case Artifact.flutterTester: 66 if (platform == TargetPlatform.windows_x64) { 67 return 'flutter_tester.exe'; 68 } 69 return 'flutter_tester'; 70 case Artifact.snapshotDart: 71 return 'snapshot.dart'; 72 case Artifact.flutterFramework: 73 return 'Flutter.framework'; 74 case Artifact.flutterMacOSFramework: 75 return 'FlutterMacOS.framework'; 76 case Artifact.vmSnapshotData: 77 return 'vm_isolate_snapshot.bin'; 78 case Artifact.isolateSnapshotData: 79 return 'isolate_snapshot.bin'; 80 case Artifact.platformKernelDill: 81 return 'platform_strong.dill'; 82 case Artifact.platformLibrariesJson: 83 return 'libraries.json'; 84 case Artifact.flutterPatchedSdkPath: 85 assert(false, 'No filename for sdk path, should not be invoked'); 86 return null; 87 case Artifact.flutterWebSdk: 88 assert(false, 'No filename for web sdk path, should not be invoked'); 89 return null; 90 case Artifact.engineDartSdkPath: 91 return 'dart-sdk'; 92 case Artifact.frontendServerSnapshotForEngineDartSdk: 93 return 'frontend_server.dart.snapshot'; 94 case Artifact.engineDartBinary: 95 return 'dart'; 96 case Artifact.dart2jsSnapshot: 97 return 'dart2js.dart.snapshot'; 98 case Artifact.dartdevcSnapshot: 99 return 'dartdevc.dart.snapshot'; 100 case Artifact.kernelWorkerSnapshot: 101 return 'kernel_worker.dart.snapshot'; 102 case Artifact.iosDeploy: 103 return 'ios-deploy'; 104 case Artifact.ideviceinfo: 105 return 'ideviceinfo'; 106 case Artifact.ideviceId: 107 return 'idevice_id'; 108 case Artifact.idevicename: 109 return 'idevicename'; 110 case Artifact.idevicesyslog: 111 return 'idevicesyslog'; 112 case Artifact.idevicescreenshot: 113 return 'idevicescreenshot'; 114 case Artifact.ideviceinstaller: 115 return 'ideviceinstaller'; 116 case Artifact.iproxy: 117 return 'iproxy'; 118 case Artifact.linuxDesktopPath: 119 return ''; 120 case Artifact.windowsDesktopPath: 121 return ''; 122 case Artifact.skyEnginePath: 123 return 'sky_engine'; 124 case Artifact.flutterMacOSPodspec: 125 return 'FlutterMacOS.podspec'; 126 } 127 assert(false, 'Invalid artifact $artifact.'); 128 return null; 129} 130 131class EngineBuildPaths { 132 const EngineBuildPaths({ 133 @required this.targetEngine, 134 @required this.hostEngine, 135 }) : assert(targetEngine != null), 136 assert(hostEngine != null); 137 138 final String targetEngine; 139 final String hostEngine; 140} 141 142// Manages the engine artifacts of Flutter. 143abstract class Artifacts { 144 static Artifacts get instance => context.get<Artifacts>(); 145 146 static LocalEngineArtifacts getLocalEngine(String engineSrcPath, EngineBuildPaths engineBuildPaths) { 147 return LocalEngineArtifacts(engineSrcPath, engineBuildPaths.targetEngine, engineBuildPaths.hostEngine); 148 } 149 150 // Returns the requested [artifact] for the [platform] and [mode] combination. 151 String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }); 152 153 // Returns which set of engine artifacts is currently used for the [platform] 154 // and [mode] combination. 155 String getEngineType(TargetPlatform platform, [ BuildMode mode ]); 156} 157 158/// Manages the engine artifacts downloaded to the local cache. 159class CachedArtifacts extends Artifacts { 160 161 @override 162 String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) { 163 platform ??= _currentHostPlatform; 164 switch (platform) { 165 case TargetPlatform.android_arm: 166 case TargetPlatform.android_arm64: 167 case TargetPlatform.android_x64: 168 case TargetPlatform.android_x86: 169 return _getAndroidArtifactPath(artifact, platform, mode); 170 case TargetPlatform.ios: 171 return _getIosArtifactPath(artifact, platform, mode); 172 case TargetPlatform.darwin_x64: 173 case TargetPlatform.linux_x64: 174 case TargetPlatform.windows_x64: 175 case TargetPlatform.fuchsia: 176 case TargetPlatform.tester: 177 case TargetPlatform.web_javascript: 178 return _getHostArtifactPath(artifact, platform, mode); 179 } 180 assert(false, 'Invalid platform $platform.'); 181 return null; 182 } 183 184 @override 185 String getEngineType(TargetPlatform platform, [ BuildMode mode ]) { 186 return fs.path.basename(_getEngineArtifactsPath(platform, mode)); 187 } 188 189 String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) { 190 final String engineDir = _getEngineArtifactsPath(platform, mode); 191 switch (artifact) { 192 case Artifact.frontendServerSnapshotForEngineDartSdk: 193 assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.'); 194 return fs.path.join(engineDir, _artifactToFileName(artifact)); 195 case Artifact.genSnapshot: 196 assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.'); 197 final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform()); 198 return fs.path.join(engineDir, hostPlatform, _artifactToFileName(artifact)); 199 default: 200 assert(false, 'Artifact $artifact not available for platform $platform.'); 201 return null; 202 } 203 } 204 205 String _getIosArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) { 206 final String artifactFileName = _artifactToFileName(artifact); 207 switch (artifact) { 208 case Artifact.genSnapshot: 209 case Artifact.snapshotDart: 210 case Artifact.flutterFramework: 211 case Artifact.frontendServerSnapshotForEngineDartSdk: 212 final String engineDir = _getEngineArtifactsPath(platform, mode); 213 return fs.path.join(engineDir, artifactFileName); 214 case Artifact.ideviceId: 215 case Artifact.ideviceinfo: 216 case Artifact.idevicescreenshot: 217 case Artifact.idevicesyslog: 218 case Artifact.idevicename: 219 return cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName).path; 220 case Artifact.iosDeploy: 221 return cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName).path; 222 case Artifact.ideviceinstaller: 223 return cache.getArtifactDirectory('ideviceinstaller').childFile(artifactFileName).path; 224 case Artifact.iproxy: 225 return cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName).path; 226 default: 227 assert(false, 'Artifact $artifact not available for platform $platform.'); 228 return null; 229 } 230 } 231 232 String _getFlutterPatchedSdkPath(BuildMode mode) { 233 final String engineArtifactsPath = cache.getArtifactDirectory('engine').path; 234 return fs.path.join(engineArtifactsPath, 'common', 235 mode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk'); 236 } 237 238 String _getFlutterWebSdkPath() { 239 return cache.getWebSdkDirectory().path; 240 } 241 242 String _getHostArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) { 243 switch (artifact) { 244 case Artifact.genSnapshot: 245 // For script snapshots any gen_snapshot binary will do. Returning gen_snapshot for 246 // android_arm in profile mode because it is available on all supported host platforms. 247 return _getAndroidArtifactPath(artifact, TargetPlatform.android_arm, BuildMode.profile); 248 case Artifact.flutterTester: 249 case Artifact.vmSnapshotData: 250 case Artifact.isolateSnapshotData: 251 case Artifact.frontendServerSnapshotForEngineDartSdk: 252 final String engineArtifactsPath = cache.getArtifactDirectory('engine').path; 253 final String platformDirName = getNameForTargetPlatform(platform); 254 return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode)); 255 case Artifact.engineDartSdkPath: 256 return dartSdkPath; 257 case Artifact.engineDartBinary: 258 return fs.path.join(dartSdkPath, 'bin', _artifactToFileName(artifact)); 259 case Artifact.platformKernelDill: 260 return fs.path.join(_getFlutterPatchedSdkPath(mode), _artifactToFileName(artifact)); 261 case Artifact.platformLibrariesJson: 262 return fs.path.join(_getFlutterPatchedSdkPath(mode), 'lib', _artifactToFileName(artifact)); 263 case Artifact.flutterPatchedSdkPath: 264 return _getFlutterPatchedSdkPath(mode); 265 case Artifact.flutterWebSdk: 266 return _getFlutterWebSdkPath(); 267 case Artifact.dart2jsSnapshot: 268 return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact)); 269 case Artifact.dartdevcSnapshot: 270 return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact)); 271 case Artifact.kernelWorkerSnapshot: 272 return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact)); 273 case Artifact.flutterMacOSFramework: 274 case Artifact.linuxDesktopPath: 275 case Artifact.windowsDesktopPath: 276 case Artifact.flutterMacOSPodspec: 277 final String engineArtifactsPath = cache.getArtifactDirectory('engine').path; 278 final String platformDirName = getNameForTargetPlatform(platform); 279 return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode)); 280 case Artifact.skyEnginePath: 281 final Directory dartPackageDirectory = cache.getCacheDir('pkg'); 282 return fs.path.join(dartPackageDirectory.path, _artifactToFileName(artifact)); 283 default: 284 assert(false, 'Artifact $artifact not available for platform $platform.'); 285 return null; 286 } 287 } 288 289 String _getEngineArtifactsPath(TargetPlatform platform, [ BuildMode mode ]) { 290 final String engineDir = cache.getArtifactDirectory('engine').path; 291 final String platformName = getNameForTargetPlatform(platform); 292 switch (platform) { 293 case TargetPlatform.linux_x64: 294 case TargetPlatform.darwin_x64: 295 case TargetPlatform.windows_x64: 296 case TargetPlatform.fuchsia: 297 case TargetPlatform.tester: 298 case TargetPlatform.web_javascript: 299 assert(mode == null, 'Platform $platform does not support different build modes.'); 300 return fs.path.join(engineDir, platformName); 301 case TargetPlatform.ios: 302 case TargetPlatform.android_arm: 303 case TargetPlatform.android_arm64: 304 case TargetPlatform.android_x64: 305 case TargetPlatform.android_x86: 306 assert(mode != null, 'Need to specify a build mode for platform $platform.'); 307 final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : ''; 308 return fs.path.join(engineDir, platformName + suffix); 309 } 310 assert(false, 'Invalid platform $platform.'); 311 return null; 312 } 313 314 TargetPlatform get _currentHostPlatform { 315 if (platform.isMacOS) 316 return TargetPlatform.darwin_x64; 317 if (platform.isLinux) 318 return TargetPlatform.linux_x64; 319 if (platform.isWindows) 320 return TargetPlatform.windows_x64; 321 throw UnimplementedError('Host OS not supported.'); 322 } 323} 324 325/// Manages the artifacts of a locally built engine. 326class LocalEngineArtifacts extends Artifacts { 327 LocalEngineArtifacts(this._engineSrcPath, this.engineOutPath, this._hostEngineOutPath); 328 329 final String _engineSrcPath; 330 final String engineOutPath; // TODO(goderbauer): This should be private. 331 final String _hostEngineOutPath; 332 333 @override 334 String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) { 335 final String artifactFileName = _artifactToFileName(artifact); 336 switch (artifact) { 337 case Artifact.snapshotDart: 338 return fs.path.join(_engineSrcPath, 'flutter', 'lib', 'snapshot', artifactFileName); 339 case Artifact.genSnapshot: 340 return _genSnapshotPath(); 341 case Artifact.flutterTester: 342 return _flutterTesterPath(platform); 343 case Artifact.isolateSnapshotData: 344 case Artifact.vmSnapshotData: 345 return fs.path.join(engineOutPath, 'gen', 'flutter', 'lib', 'snapshot', artifactFileName); 346 case Artifact.platformKernelDill: 347 return fs.path.join(_getFlutterPatchedSdkPath(mode), artifactFileName); 348 case Artifact.platformLibrariesJson: 349 return fs.path.join(_getFlutterPatchedSdkPath(mode), 'lib', artifactFileName); 350 case Artifact.flutterFramework: 351 return fs.path.join(engineOutPath, artifactFileName); 352 case Artifact.flutterMacOSFramework: 353 return fs.path.join(engineOutPath, artifactFileName); 354 case Artifact.flutterPatchedSdkPath: 355 // When using local engine always use [BuildMode.debug] regardless of 356 // what was specified in [mode] argument because local engine will 357 // have only one flutter_patched_sdk in standard location, that 358 // is happen to be what debug(non-release) mode is using. 359 return _getFlutterPatchedSdkPath(BuildMode.debug); 360 case Artifact.flutterWebSdk: 361 return _getFlutterWebSdkPath(); 362 case Artifact.frontendServerSnapshotForEngineDartSdk: 363 return fs.path.join(_hostEngineOutPath, 'gen', artifactFileName); 364 case Artifact.engineDartSdkPath: 365 return fs.path.join(_hostEngineOutPath, 'dart-sdk'); 366 case Artifact.engineDartBinary: 367 return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', artifactFileName); 368 case Artifact.dart2jsSnapshot: 369 return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName); 370 case Artifact.dartdevcSnapshot: 371 return fs.path.join(dartSdkPath, 'bin', 'snapshots', artifactFileName); 372 case Artifact.kernelWorkerSnapshot: 373 return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName); 374 case Artifact.ideviceId: 375 case Artifact.ideviceinfo: 376 case Artifact.idevicename: 377 case Artifact.idevicescreenshot: 378 case Artifact.idevicesyslog: 379 return cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName).path; 380 case Artifact.ideviceinstaller: 381 return cache.getArtifactDirectory('ideviceinstaller').childFile(artifactFileName).path; 382 case Artifact.iosDeploy: 383 return cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName).path; 384 case Artifact.iproxy: 385 return cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName).path; 386 case Artifact.linuxDesktopPath: 387 return fs.path.join(_hostEngineOutPath, artifactFileName); 388 case Artifact.windowsDesktopPath: 389 return fs.path.join(_hostEngineOutPath, artifactFileName); 390 case Artifact.skyEnginePath: 391 return fs.path.join(_hostEngineOutPath, 'gen', 'dart-pkg', artifactFileName); 392 case Artifact.flutterMacOSPodspec: 393 return fs.path.join(_hostEngineOutPath, _artifactToFileName(artifact)); 394 } 395 assert(false, 'Invalid artifact $artifact.'); 396 return null; 397 } 398 399 @override 400 String getEngineType(TargetPlatform platform, [ BuildMode mode ]) { 401 return fs.path.basename(engineOutPath); 402 } 403 404 String _getFlutterPatchedSdkPath(BuildMode buildMode) { 405 return fs.path.join(engineOutPath, 406 buildMode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk'); 407 } 408 409 String _getFlutterWebSdkPath() { 410 return fs.path.join(engineOutPath, 'flutter_web_sdk'); 411 } 412 413 String _genSnapshotPath() { 414 const List<String> clangDirs = <String>['.', 'clang_x64', 'clang_x86', 'clang_i386']; 415 final String genSnapshotName = _artifactToFileName(Artifact.genSnapshot); 416 for (String clangDir in clangDirs) { 417 final String genSnapshotPath = fs.path.join(engineOutPath, clangDir, genSnapshotName); 418 if (processManager.canRun(genSnapshotPath)) 419 return genSnapshotPath; 420 } 421 throw Exception('Unable to find $genSnapshotName'); 422 } 423 424 String _flutterTesterPath(TargetPlatform platform) { 425 if (getCurrentHostPlatform() == HostPlatform.linux_x64) { 426 return fs.path.join(engineOutPath, _artifactToFileName(Artifact.flutterTester)); 427 } else if (getCurrentHostPlatform() == HostPlatform.darwin_x64) { 428 return fs.path.join(engineOutPath, 'flutter_tester'); 429 } else if (getCurrentHostPlatform() == HostPlatform.windows_x64) { 430 return fs.path.join(engineOutPath, 'flutter_tester.exe'); 431 } 432 throw Exception('Unsupported platform $platform.'); 433 } 434} 435 436/// An implementation of [Artifacts] that provides individual overrides. 437/// 438/// If an artifact is not provided, the lookup delegates to the parent. 439class OverrideArtifacts implements Artifacts { 440 /// Creates a new [OverrideArtifacts]. 441 /// 442 /// [parent] must be provided. 443 OverrideArtifacts({ 444 @required this.parent, 445 this.frontendServer, 446 this.engineDartBinary, 447 this.platformKernelDill, 448 this.flutterPatchedSdk, 449 }) : assert(parent != null); 450 451 final Artifacts parent; 452 final File frontendServer; 453 final File engineDartBinary; 454 final File platformKernelDill; 455 final File flutterPatchedSdk; 456 457 @override 458 String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) { 459 if (artifact == Artifact.frontendServerSnapshotForEngineDartSdk && frontendServer != null) { 460 return frontendServer.path; 461 } 462 if (artifact == Artifact.engineDartBinary && engineDartBinary != null) { 463 return engineDartBinary.path; 464 } 465 if (artifact == Artifact.platformKernelDill && platformKernelDill != null) { 466 return platformKernelDill.path; 467 } 468 if (artifact == Artifact.flutterPatchedSdkPath && flutterPatchedSdk != null) { 469 return flutterPatchedSdk.path; 470 } 471 return parent.getArtifactPath(artifact, platform: platform, mode: mode); 472 } 473 474 @override 475 String getEngineType(TargetPlatform platform, [ BuildMode mode ]) => parent.getEngineType(platform, mode); 476} 477