web_compilation_delegate.dart 6.23 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
// ignore_for_file: implementation_imports
6
import 'dart:async';
7
import 'dart:io' as io; // ignore: dart_io_import
8

9
import 'package:build/build.dart';
10 11
import 'package:build_daemon/client.dart';
import 'package:build_daemon/data/build_status.dart';
12
import 'package:build_runner_core/build_runner_core.dart' as core;
13
import 'package:glob/glob.dart';
14 15 16
import 'package:path/path.dart' as path;

import '../base/file_system.dart';
17
import '../build_info.dart';
18
import '../convert.dart';
19 20 21
import '../platform_plugins.dart';
import '../plugins.dart';
import '../project.dart';
22
import '../web/compile.dart';
23
import 'web_fs.dart';
24 25 26 27 28 29

/// A build_runner specific implementation of the [WebCompilationProxy].
class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
  BuildRunnerWebCompilationProxy();

  @override
30
  Future<bool> initialize({
31
    Directory projectDirectory,
32
    String testOutputDir,
33
    List<String> testFiles,
34
    BuildMode mode,
35 36
    String projectName,
    bool initializePlatform,
37
  }) async {
38
    // Create the .dart_tool directory if it doesn't exist.
39
    projectDirectory
40
      .childDirectory('.dart_tool')
41
      .createSync();
42 43 44
    final FlutterProject flutterProject = FlutterProject.fromDirectory(projectDirectory);
    final bool hasWebPlugins = findPlugins(flutterProject)
        .any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
45
    final BuildDaemonClient client = await buildDaemonCreator.startBuildDaemon(
46
      projectDirectory.path,
47 48
      release: mode == BuildMode.release,
      profile: mode == BuildMode.profile,
49 50
      hasPlugins: hasWebPlugins,
      initializePlatform: initializePlatform,
51 52 53 54 55 56 57 58
      testTargets: WebTestTargetManifest(
        testFiles
          .map<String>((String absolutePath) {
            final String relativePath = path.relative(absolutePath, from: projectDirectory.path);
            return '${path.withoutExtension(relativePath)}.*';
          })
          .toList(),
      ),
59 60 61 62 63 64 65 66 67 68 69 70 71
    );
    client.startBuild();
    bool success = true;
    await for (BuildResults results in client.buildResults) {
      final BuildResult result = results.results.firstWhere((BuildResult result) {
        return result.target == 'web';
      });
      if (result.status == BuildStatus.failed) {
        success = false;
        break;
      }
      if (result.status == BuildStatus.succeeded) {
        break;
72
      }
73 74 75 76 77 78 79 80 81 82 83 84 85 86
    }
    if (success && testOutputDir != null) {
      final Directory rootDirectory = projectDirectory
        .childDirectory('.dart_tool')
        .childDirectory('build')
        .childDirectory('flutter_web');

      final Iterable<Directory> childDirectories = rootDirectory
        .listSync()
        .whereType<Directory>();
      for (Directory childDirectory in childDirectories) {
        final String path = fs.path.join(testOutputDir, 'packages',
            fs.path.basename(childDirectory.path));
        copyDirectorySync(childDirectory.childDirectory('lib'), fs.directory(path));
87
      }
88 89 90 91
      final Directory outputDirectory = rootDirectory
          .childDirectory(projectName)
          .childDirectory('test');
      copyDirectorySync(outputDirectory, fs.directory(fs.path.join(testOutputDir)));
92
    }
93
    return success;
94
  }
95
}
96

97
/// Handles mapping a single root file scheme to a multi-root scheme.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
///
/// This allows one build_runner build to read the output from a previous
/// isolated build.
class MultirootFileBasedAssetReader extends core.FileBasedAssetReader {
  MultirootFileBasedAssetReader(
    core.PackageGraph packageGraph,
    this.generatedDirectory,
  ) : super(packageGraph);

  final Directory generatedDirectory;

  @override
  Future<bool> canRead(AssetId id) {
    if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
      return _generatedFile(id).exists();
    }
    return super.canRead(id);
  }

  @override
  Future<List<int>> readAsBytes(AssetId id) {
    if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
      return _generatedFile(id).readAsBytes();
    }
    return super.readAsBytes(id);
  }

  @override
  Future<String> readAsString(AssetId id, {Encoding encoding}) {
    if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
      return _generatedFile(id).readAsString();
    }
    return super.readAsString(id, encoding: encoding);
  }

  @override
  Stream<AssetId> findAssets(Glob glob, {String package}) async* {
    if (package == null || packageGraph.root.name == package) {
136
      final String generatedRoot = fs.path.join(generatedDirectory.path, packageGraph.root.name);
137
      await for (io.FileSystemEntity entity in glob.list(followLinks: true, root: packageGraph.root.path)) {
138
        if (entity is io.File && _isNotHidden(entity) && !fs.path.isWithin(generatedRoot, entity.path)) {
139 140 141 142 143 144 145 146
          yield _fileToAssetId(entity, packageGraph.root);
        }
      }
      if (!fs.isDirectorySync(generatedRoot)) {
        return;
      }
      await for (io.FileSystemEntity entity in glob.list(followLinks: true, root: generatedRoot)) {
        if (entity is io.File && _isNotHidden(entity)) {
147
          yield _fileToAssetId(entity, packageGraph.root, fs.path.relative(generatedRoot), true);
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
        }
      }
      return;
    }
    yield* super.findAssets(glob, package: package);
  }

  bool _isNotHidden(io.FileSystemEntity entity) {
    return !path.basename(entity.path).startsWith('._');
  }

  bool _missingSource(AssetId id) {
    return !fs.file(path.joinAll(<String>[packageGraph.root.path, ...id.pathSegments])).existsSync();
  }

  File _generatedFile(AssetId id) {
    return fs.file(
      path.joinAll(<String>[generatedDirectory.path, packageGraph.root.name, ...id.pathSegments])
    );
  }

  /// Creates an [AssetId] for [file], which is a part of [packageNode].
170
  AssetId _fileToAssetId(io.File file, core.PackageNode packageNode, [String root, bool generated = false]) {
171
    final String filePath = path.normalize(file.absolute.path);
172 173 174 175 176 177
    String relativePath;
    if (generated) {
      relativePath = filePath.substring(root.length + 2);
    } else {
      relativePath = path.relative(filePath, from: packageNode.path);
    }
178 179 180
    return AssetId(packageNode.name, relativePath);
  }
}