mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
Flutter assemble for macos take 2! (#36987)
This commit is contained in:
parent
dd1fb3bcb7
commit
2ab469952b
|
@ -3,8 +3,6 @@
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
# TODO(jonahwilliams): refactor this and xcode_backend.sh into one script
|
|
||||||
# once macOS supports the same configuration as iOS.
|
|
||||||
RunCommand() {
|
RunCommand() {
|
||||||
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
|
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
|
||||||
echo "♦ $*"
|
echo "♦ $*"
|
||||||
|
@ -33,18 +31,6 @@ if [[ -n "$FLUTTER_TARGET" ]]; then
|
||||||
target_path="${FLUTTER_TARGET}"
|
target_path="${FLUTTER_TARGET}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set the track widget creation flag.
|
|
||||||
track_widget_creation_flag=""
|
|
||||||
if [[ -n "$TRACK_WIDGET_CREATION" ]]; then
|
|
||||||
track_widget_creation_flag="--track-widget-creation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy the framework and handle local engine builds.
|
|
||||||
framework_name="FlutterMacOS.framework"
|
|
||||||
ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral"
|
|
||||||
framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/darwin-x64"
|
|
||||||
flutter_framework="${framework_path}/${framework_name}"
|
|
||||||
|
|
||||||
if [[ -n "$FLUTTER_ENGINE" ]]; then
|
if [[ -n "$FLUTTER_ENGINE" ]]; then
|
||||||
flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}"
|
flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}"
|
||||||
fi
|
fi
|
||||||
|
@ -63,22 +49,40 @@ if [[ -n "$LOCAL_ENGINE" ]]; then
|
||||||
exit -1
|
exit -1
|
||||||
fi
|
fi
|
||||||
local_engine_flag="--local-engine=${LOCAL_ENGINE}"
|
local_engine_flag="--local-engine=${LOCAL_ENGINE}"
|
||||||
flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/${framework_name}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RunCommand mkdir -p -- "$ephemeral_dir"
|
|
||||||
RunCommand rm -rf -- "${ephemeral_dir}/${framework_name}"
|
|
||||||
RunCommand cp -Rp -- "${flutter_framework}" "${ephemeral_dir}"
|
|
||||||
|
|
||||||
# Set the build mode
|
# Set the build mode
|
||||||
build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
|
build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
|
||||||
|
case "$build_mode" in
|
||||||
|
debug)
|
||||||
|
build_target="debug_macos_application"
|
||||||
|
;;
|
||||||
|
profile)
|
||||||
|
build_target="profile_macos_application"
|
||||||
|
;;
|
||||||
|
release)
|
||||||
|
build_target="release_macos_application"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
EchoError "Unknown build mode ${build_mode}"
|
||||||
|
exit -1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# The path where the input/output xcfilelists are stored. These are used by xcode
|
||||||
|
# to conditionally skip this script phase if neither have changed.
|
||||||
|
build_inputs_path="${SOURCE_ROOT}/Flutter/ephemeral/FlutterInputs.xcfilelist"
|
||||||
|
build_outputs_path="${SOURCE_ROOT}/Flutter/ephemeral/FlutterOutputs.xcfilelist"
|
||||||
|
|
||||||
|
# TODO(jonahwilliams): support flavors https://github.com/flutter/flutter/issues/32923
|
||||||
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
|
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
|
||||||
${verbose_flag} \
|
${verbose_flag} \
|
||||||
build bundle \
|
|
||||||
--target-platform=darwin-x64 \
|
|
||||||
--target="${target_path}" \
|
|
||||||
--${build_mode} \
|
|
||||||
${track_widget_creation_flag} \
|
|
||||||
${flutter_engine_flag} \
|
${flutter_engine_flag} \
|
||||||
${local_engine_flag}
|
${local_engine_flag} \
|
||||||
|
assemble \
|
||||||
|
-dTargetFile="${target_path}" \
|
||||||
|
-dTargetPlatform=darwin-x64 \
|
||||||
|
-dBuildMode="${build_mode}" \
|
||||||
|
--build-inputs="${build_inputs_path}" \
|
||||||
|
--build-outputs="${build_outputs_path}" \
|
||||||
|
"${build_target}"
|
||||||
|
|
|
@ -54,6 +54,8 @@ enum Artifact {
|
||||||
windowsDesktopPath,
|
windowsDesktopPath,
|
||||||
/// The root of the sky_engine package
|
/// The root of the sky_engine package
|
||||||
skyEnginePath,
|
skyEnginePath,
|
||||||
|
/// The location of the macOS engine podspec file.
|
||||||
|
flutterMacOSPodspec,
|
||||||
}
|
}
|
||||||
|
|
||||||
String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMode mode ]) {
|
String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMode mode ]) {
|
||||||
|
@ -119,6 +121,8 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo
|
||||||
return '';
|
return '';
|
||||||
case Artifact.skyEnginePath:
|
case Artifact.skyEnginePath:
|
||||||
return 'sky_engine';
|
return 'sky_engine';
|
||||||
|
case Artifact.flutterMacOSPodspec:
|
||||||
|
return 'FlutterMacOS.podspec';
|
||||||
}
|
}
|
||||||
assert(false, 'Invalid artifact $artifact.');
|
assert(false, 'Invalid artifact $artifact.');
|
||||||
return null;
|
return null;
|
||||||
|
@ -269,6 +273,7 @@ class CachedArtifacts extends Artifacts {
|
||||||
case Artifact.flutterMacOSFramework:
|
case Artifact.flutterMacOSFramework:
|
||||||
case Artifact.linuxDesktopPath:
|
case Artifact.linuxDesktopPath:
|
||||||
case Artifact.windowsDesktopPath:
|
case Artifact.windowsDesktopPath:
|
||||||
|
case Artifact.flutterMacOSPodspec:
|
||||||
final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
|
final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
|
||||||
final String platformDirName = getNameForTargetPlatform(platform);
|
final String platformDirName = getNameForTargetPlatform(platform);
|
||||||
return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
|
return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
|
||||||
|
@ -384,6 +389,8 @@ class LocalEngineArtifacts extends Artifacts {
|
||||||
return fs.path.join(_hostEngineOutPath, artifactFileName);
|
return fs.path.join(_hostEngineOutPath, artifactFileName);
|
||||||
case Artifact.skyEnginePath:
|
case Artifact.skyEnginePath:
|
||||||
return fs.path.join(_hostEngineOutPath, 'gen', 'dart-pkg', artifactFileName);
|
return fs.path.join(_hostEngineOutPath, 'gen', 'dart-pkg', artifactFileName);
|
||||||
|
case Artifact.flutterMacOSPodspec:
|
||||||
|
return fs.path.join(_hostEngineOutPath, _artifactToFileName(artifact));
|
||||||
}
|
}
|
||||||
assert(false, 'Invalid artifact $artifact.');
|
assert(false, 'Invalid artifact $artifact.');
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -171,7 +171,7 @@ abstract class Target {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each outut, first determine if we've already computed the hash
|
// For each output, first determine if we've already computed the hash
|
||||||
// for it. Then collect it to be sent off for hashing as a group.
|
// for it. Then collect it to be sent off for hashing as a group.
|
||||||
for (String previousOutput in previousOutputs) {
|
for (String previousOutput in previousOutputs) {
|
||||||
final File file = fs.file(previousOutput);
|
final File file = fs.file(previousOutput);
|
||||||
|
@ -436,11 +436,19 @@ class Environment {
|
||||||
|
|
||||||
/// The result information from the build system.
|
/// The result information from the build system.
|
||||||
class BuildResult {
|
class BuildResult {
|
||||||
BuildResult(this.success, this.exceptions, this.performance);
|
BuildResult({
|
||||||
|
@required this.success,
|
||||||
|
this.exceptions = const <String, ExceptionMeasurement>{},
|
||||||
|
this.performance = const <String, PerformanceMeasurement>{},
|
||||||
|
this.inputFiles = const <File>[],
|
||||||
|
this.outputFiles = const <File>[],
|
||||||
|
});
|
||||||
|
|
||||||
final bool success;
|
final bool success;
|
||||||
final Map<String, ExceptionMeasurement> exceptions;
|
final Map<String, ExceptionMeasurement> exceptions;
|
||||||
final Map<String, PerformanceMeasurement> performance;
|
final Map<String, PerformanceMeasurement> performance;
|
||||||
|
final List<File> inputFiles;
|
||||||
|
final List<File> outputFiles;
|
||||||
|
|
||||||
bool get hasException => exceptions.isNotEmpty;
|
bool get hasException => exceptions.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
@ -472,10 +480,31 @@ class BuildSystem {
|
||||||
// Always persist the file cache to disk.
|
// Always persist the file cache to disk.
|
||||||
fileCache.persist();
|
fileCache.persist();
|
||||||
}
|
}
|
||||||
|
// TODO(jonahwilliams): this is a bit of a hack, due to various parts of
|
||||||
|
// the flutter tool writing these files unconditionally. Since Xcode uses
|
||||||
|
// timestamps to track files, this leads to unecessary rebuilds if they
|
||||||
|
// are included. Once all the places that write these files have been
|
||||||
|
// tracked down and moved into assemble, these checks should be removable.
|
||||||
|
{
|
||||||
|
buildInstance.inputFiles.removeWhere((String path, File file) {
|
||||||
|
return path.contains('pubspec.yaml') ||
|
||||||
|
path.contains('.flutter-plugins') ||
|
||||||
|
path.contains('xcconfig');
|
||||||
|
});
|
||||||
|
buildInstance.outputFiles.removeWhere((String path, File file) {
|
||||||
|
return path.contains('pubspec.yaml') ||
|
||||||
|
path.contains('.flutter-plugins') ||
|
||||||
|
path.contains('xcconfig');
|
||||||
|
});
|
||||||
|
}
|
||||||
return BuildResult(
|
return BuildResult(
|
||||||
passed,
|
success: passed,
|
||||||
buildInstance.exceptionMeasurements,
|
exceptions: buildInstance.exceptionMeasurements,
|
||||||
buildInstance.stepTimings,
|
performance: buildInstance.stepTimings,
|
||||||
|
inputFiles: buildInstance.inputFiles.values.toList()
|
||||||
|
..sort((File a, File b) => a.path.compareTo(b.path)),
|
||||||
|
outputFiles: buildInstance.outputFiles.values.toList()
|
||||||
|
..sort((File a, File b) => a.path.compareTo(b.path)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,6 +519,8 @@ class _BuildInstance {
|
||||||
final Map<String, AsyncMemoizer<void>> pending = <String, AsyncMemoizer<void>>{};
|
final Map<String, AsyncMemoizer<void>> pending = <String, AsyncMemoizer<void>>{};
|
||||||
final Environment environment;
|
final Environment environment;
|
||||||
final FileHashStore fileCache;
|
final FileHashStore fileCache;
|
||||||
|
final Map<String, File> inputFiles = <String, File>{};
|
||||||
|
final Map<String, File> outputFiles = <String, File>{};
|
||||||
|
|
||||||
// Timings collected during target invocation.
|
// Timings collected during target invocation.
|
||||||
final Map<String, PerformanceMeasurement> stepTimings = <String, PerformanceMeasurement>{};
|
final Map<String, PerformanceMeasurement> stepTimings = <String, PerformanceMeasurement>{};
|
||||||
|
@ -514,18 +545,41 @@ class _BuildInstance {
|
||||||
try {
|
try {
|
||||||
final List<File> inputs = target.resolveInputs(environment);
|
final List<File> inputs = target.resolveInputs(environment);
|
||||||
final bool canSkip = await target.computeChanges(inputs, environment, fileCache);
|
final bool canSkip = await target.computeChanges(inputs, environment, fileCache);
|
||||||
|
for (File input in inputs) {
|
||||||
|
// The build system should produce a list of aggregate input and output
|
||||||
|
// files for the overall build. The goal is to provide this to a hosting
|
||||||
|
// build system, such as Xcode, to configure logic for when to skip the
|
||||||
|
// rule/phase which contains the flutter build. When looking at the
|
||||||
|
// inputs and outputs for the individual rules, we need to be careful to
|
||||||
|
// remove inputs that were actually output from previous build steps.
|
||||||
|
// This indicates that the file is actual an output or intermediary. If
|
||||||
|
// these files are included as both inputs and outputs then it isn't
|
||||||
|
// possible to construct a DAG describing the build.
|
||||||
|
final String resolvedPath = input.resolveSymbolicLinksSync();
|
||||||
|
if (outputFiles.containsKey(resolvedPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inputFiles[resolvedPath] = input;
|
||||||
|
}
|
||||||
if (canSkip) {
|
if (canSkip) {
|
||||||
skipped = true;
|
skipped = true;
|
||||||
printStatus('Skipping target: ${target.name}');
|
printStatus('Skipping target: ${target.name}');
|
||||||
|
final List<File> outputs = target.resolveOutputs(environment, implicit: true);
|
||||||
|
for (File output in outputs) {
|
||||||
|
outputFiles[output.resolveSymbolicLinksSync()] = output;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
printStatus('${target.name}: Starting');
|
printStatus('${target.name}: Starting');
|
||||||
await target.build(inputs, environment);
|
await target.build(inputs, environment);
|
||||||
printStatus('${target.name}: Complete');
|
printStatus('${target.name}: Complete');
|
||||||
|
|
||||||
final List<File> outputs = target.resolveOutputs(environment);
|
final List<File> outputs = target.resolveOutputs(environment, implicit: true);
|
||||||
// Update hashes for output files.
|
// Update hashes for output files.
|
||||||
await fileCache.hashFiles(outputs);
|
await fileCache.hashFiles(outputs);
|
||||||
target._writeStamp(inputs, outputs, environment);
|
target._writeStamp(inputs, outputs, environment);
|
||||||
|
for (File output in outputs) {
|
||||||
|
outputFiles[output.resolveSymbolicLinksSync()] = output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (exception, stackTrace) {
|
} catch (exception, stackTrace) {
|
||||||
target.clearStamp(environment);
|
target.clearStamp(environment);
|
||||||
|
@ -554,10 +608,10 @@ class ExceptionMeasurement {
|
||||||
|
|
||||||
/// Helper class to collect measurement data.
|
/// Helper class to collect measurement data.
|
||||||
class PerformanceMeasurement {
|
class PerformanceMeasurement {
|
||||||
PerformanceMeasurement(this.target, this.elapsedMilliseconds, this.skiped, this.passed);
|
PerformanceMeasurement(this.target, this.elapsedMilliseconds, this.skipped, this.passed);
|
||||||
final int elapsedMilliseconds;
|
final int elapsedMilliseconds;
|
||||||
final String target;
|
final String target;
|
||||||
final bool skiped;
|
final bool skipped;
|
||||||
final bool passed;
|
final bool passed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,10 @@ class SourceVisitor {
|
||||||
|
|
||||||
/// Visit a [Source] which contains a file uri.
|
/// Visit a [Source] which contains a file uri.
|
||||||
///
|
///
|
||||||
/// The uri may that may include constants defined in an [Environment].
|
/// The uri may include constants defined in an [Environment]. If
|
||||||
void visitPattern(String pattern) {
|
/// [optional] is true, the file is not required to exist. In this case, it
|
||||||
|
/// is never resolved as an input.
|
||||||
|
void visitPattern(String pattern, bool optional) {
|
||||||
// perform substitution of the environmental values and then
|
// perform substitution of the environmental values and then
|
||||||
// of the local values.
|
// of the local values.
|
||||||
final List<String> segments = <String>[];
|
final List<String> segments = <String>[];
|
||||||
|
@ -74,38 +76,41 @@ class SourceVisitor {
|
||||||
}
|
}
|
||||||
rawParts.skip(1).forEach(segments.add);
|
rawParts.skip(1).forEach(segments.add);
|
||||||
final String filePath = fs.path.joinAll(segments);
|
final String filePath = fs.path.joinAll(segments);
|
||||||
if (hasWildcard) {
|
if (!hasWildcard) {
|
||||||
// Perform a simple match by splitting the wildcard containing file one
|
if (optional && !fs.isFileSync(filePath)) {
|
||||||
// the `*`. For example, for `/*.dart`, we get [.dart]. We then check
|
return;
|
||||||
// that part of the file matches. If there are values before and after
|
|
||||||
// the `*` we need to check that both match without overlapping. For
|
|
||||||
// example, `foo_*_.dart`. We want to match `foo_b_.dart` but not
|
|
||||||
// `foo_.dart`. To do so, we first subtract the first section from the
|
|
||||||
// string if the first segment matches.
|
|
||||||
final List<String> segments = wildcardFile.split('*');
|
|
||||||
if (segments.length > 2) {
|
|
||||||
throw InvalidPatternException(pattern);
|
|
||||||
}
|
}
|
||||||
if (!fs.directory(filePath).existsSync()) {
|
sources.add(fs.file(fs.path.normalize(filePath)));
|
||||||
throw Exception('$filePath does not exist!');
|
return;
|
||||||
}
|
}
|
||||||
for (FileSystemEntity entity in fs.directory(filePath).listSync()) {
|
// Perform a simple match by splitting the wildcard containing file one
|
||||||
final String filename = fs.path.basename(entity.path);
|
// the `*`. For example, for `/*.dart`, we get [.dart]. We then check
|
||||||
if (segments.isEmpty) {
|
// that part of the file matches. If there are values before and after
|
||||||
sources.add(fs.file(entity.absolute));
|
// the `*` we need to check that both match without overlapping. For
|
||||||
} else if (segments.length == 1) {
|
// example, `foo_*_.dart`. We want to match `foo_b_.dart` but not
|
||||||
if (filename.startsWith(segments[0]) ||
|
// `foo_.dart`. To do so, we first subtract the first section from the
|
||||||
filename.endsWith(segments[0])) {
|
// string if the first segment matches.
|
||||||
sources.add(entity.absolute);
|
final List<String> wildcardSegments = wildcardFile.split('*');
|
||||||
}
|
if (wildcardSegments.length > 2) {
|
||||||
} else if (filename.startsWith(segments[0])) {
|
throw InvalidPatternException(pattern);
|
||||||
if (filename.substring(segments[0].length).endsWith(segments[1])) {
|
}
|
||||||
sources.add(entity.absolute);
|
if (!fs.directory(filePath).existsSync()) {
|
||||||
}
|
throw Exception('$filePath does not exist!');
|
||||||
|
}
|
||||||
|
for (FileSystemEntity entity in fs.directory(filePath).listSync()) {
|
||||||
|
final String filename = fs.path.basename(entity.path);
|
||||||
|
if (wildcardSegments.isEmpty) {
|
||||||
|
sources.add(fs.file(entity.absolute));
|
||||||
|
} else if (wildcardSegments.length == 1) {
|
||||||
|
if (filename.startsWith(wildcardSegments[0]) ||
|
||||||
|
filename.endsWith(wildcardSegments[0])) {
|
||||||
|
sources.add(entity.absolute);
|
||||||
|
}
|
||||||
|
} else if (filename.startsWith(wildcardSegments[0])) {
|
||||||
|
if (filename.substring(wildcardSegments[0].length).endsWith(wildcardSegments[1])) {
|
||||||
|
sources.add(entity.absolute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
sources.add(fs.file(fs.path.normalize(filePath)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +144,7 @@ class SourceVisitor {
|
||||||
abstract class Source {
|
abstract class Source {
|
||||||
/// This source is a file-uri which contains some references to magic
|
/// This source is a file-uri which contains some references to magic
|
||||||
/// environment variables.
|
/// environment variables.
|
||||||
const factory Source.pattern(String pattern) = _PatternSource;
|
const factory Source.pattern(String pattern, { bool optional }) = _PatternSource;
|
||||||
|
|
||||||
/// This source is produced by invoking the provided function.
|
/// This source is produced by invoking the provided function.
|
||||||
const factory Source.function(InputFunction function) = _FunctionSource;
|
const factory Source.function(InputFunction function) = _FunctionSource;
|
||||||
|
@ -203,12 +208,13 @@ class _FunctionSource implements Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PatternSource implements Source {
|
class _PatternSource implements Source {
|
||||||
const _PatternSource(this.value);
|
const _PatternSource(this.value, { this.optional = false });
|
||||||
|
|
||||||
final String value;
|
final String value;
|
||||||
|
final bool optional;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void accept(SourceVisitor visitor) => visitor.visitPattern(value);
|
void accept(SourceVisitor visitor) => visitor.visitPattern(value, optional);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get implicit => value.contains('*');
|
bool get implicit => value.contains('*');
|
||||||
|
|
|
@ -7,6 +7,8 @@ import 'package:pool/pool.dart';
|
||||||
import '../../asset.dart';
|
import '../../asset.dart';
|
||||||
import '../../base/file_system.dart';
|
import '../../base/file_system.dart';
|
||||||
import '../../devfs.dart';
|
import '../../devfs.dart';
|
||||||
|
import '../../plugins.dart';
|
||||||
|
import '../../project.dart';
|
||||||
import '../build_system.dart';
|
import '../build_system.dart';
|
||||||
|
|
||||||
/// The copying logic for flutter assets.
|
/// The copying logic for flutter assets.
|
||||||
|
@ -100,3 +102,45 @@ class CopyAssets extends Target {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rewrites the `.flutter-plugins` file of [project] based on the plugin
|
||||||
|
/// dependencies declared in `pubspec.yaml`.
|
||||||
|
// TODO(jonahwiliams): this should be per platform and located in build
|
||||||
|
// outputs.
|
||||||
|
class FlutterPlugins extends Target {
|
||||||
|
const FlutterPlugins();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'flutter_plugins';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Target> get dependencies => const <Target>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/assets.dart'),
|
||||||
|
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
|
Source.pattern('{PROJECT_DIR}/.flutter-plugins')
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
|
// The pubspec may change for reasons other than plugins changing, so we compare
|
||||||
|
// the manifest before writing. Some hosting build systems use timestamps
|
||||||
|
// so we need to be careful to avoid tricking them into doing more work than
|
||||||
|
// necessary.
|
||||||
|
final FlutterProject project = FlutterProject.fromDirectory(environment.projectDir);
|
||||||
|
final List<Plugin> plugins = findPlugins(project);
|
||||||
|
final String pluginManifest = plugins
|
||||||
|
.map<String>((Plugin p) => '${p.name}=${escapePath(p.path)}')
|
||||||
|
.join('\n');
|
||||||
|
final File flutterPluginsFile = environment.projectDir.childFile('.flutter-plugins');
|
||||||
|
if (!flutterPluginsFile.existsSync() || flutterPluginsFile.readAsStringSync() != pluginManifest) {
|
||||||
|
flutterPluginsFile.writeAsStringSync(pluginManifest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -86,6 +86,9 @@ class KernelSnapshot extends Target {
|
||||||
}
|
}
|
||||||
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
||||||
final String targetFile = environment.defines[kTargetFile] ?? fs.path.join('lib', 'main.dart');
|
final String targetFile = environment.defines[kTargetFile] ?? fs.path.join('lib', 'main.dart');
|
||||||
|
final String packagesPath = environment.projectDir.childFile('.packages').path;
|
||||||
|
final PackageUriMapper packageUriMapper = PackageUriMapper(targetFile,
|
||||||
|
packagesPath, null, null);
|
||||||
|
|
||||||
final CompilerOutput output = await compiler.compile(
|
final CompilerOutput output = await compiler.compile(
|
||||||
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
|
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
|
||||||
|
@ -95,7 +98,8 @@ class KernelSnapshot extends Target {
|
||||||
targetProductVm: buildMode == BuildMode.release,
|
targetProductVm: buildMode == BuildMode.release,
|
||||||
outputFilePath: environment.buildDir.childFile('app.dill').path,
|
outputFilePath: environment.buildDir.childFile('app.dill').path,
|
||||||
depFilePath: null,
|
depFilePath: null,
|
||||||
mainPath: targetFile,
|
packagesPath: packagesPath,
|
||||||
|
mainPath: packageUriMapper.map(targetFile)?.toString() ?? targetFile,
|
||||||
);
|
);
|
||||||
if (output.errorCount != 0) {
|
if (output.errorCount != 0) {
|
||||||
throw Exception('Errors during snapshot creation: $output');
|
throw Exception('Errors during snapshot creation: $output');
|
||||||
|
|
|
@ -6,10 +6,16 @@ import '../../artifacts.dart';
|
||||||
import '../../base/file_system.dart';
|
import '../../base/file_system.dart';
|
||||||
import '../../base/io.dart';
|
import '../../base/io.dart';
|
||||||
import '../../base/process_manager.dart';
|
import '../../base/process_manager.dart';
|
||||||
|
import '../../build_info.dart';
|
||||||
import '../../globals.dart';
|
import '../../globals.dart';
|
||||||
|
import '../../macos/cocoapods.dart';
|
||||||
|
import '../../project.dart';
|
||||||
import '../build_system.dart';
|
import '../build_system.dart';
|
||||||
|
import '../exceptions.dart';
|
||||||
|
import 'assets.dart';
|
||||||
|
import 'dart.dart';
|
||||||
|
|
||||||
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/FlutterMacOS.framework';
|
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/ephemeral/FlutterMacOS.framework';
|
||||||
|
|
||||||
/// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
|
/// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
|
||||||
///
|
///
|
||||||
|
@ -63,6 +69,7 @@ class UnpackMacOS extends Target {
|
||||||
.projectDir
|
.projectDir
|
||||||
.childDirectory('macos')
|
.childDirectory('macos')
|
||||||
.childDirectory('Flutter')
|
.childDirectory('Flutter')
|
||||||
|
.childDirectory('ephemeral')
|
||||||
.childDirectory('FlutterMacOS.framework');
|
.childDirectory('FlutterMacOS.framework');
|
||||||
if (targetDirectory.existsSync()) {
|
if (targetDirectory.existsSync()) {
|
||||||
targetDirectory.deleteSync(recursive: true);
|
targetDirectory.deleteSync(recursive: true);
|
||||||
|
@ -78,3 +85,112 @@ class UnpackMacOS extends Target {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tell cocoapods to re-fetch dependencies.
|
||||||
|
class DebugMacOSPodInstall extends Target {
|
||||||
|
const DebugMacOSPodInstall();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'debug_macos_pod_install';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.artifact(Artifact.flutterMacOSPodspec,
|
||||||
|
platform: TargetPlatform.darwin_x64,
|
||||||
|
mode: BuildMode.debug
|
||||||
|
),
|
||||||
|
Source.pattern('{PROJECT_DIR}/macos/Podfile', optional: true),
|
||||||
|
Source.pattern('{PROJECT_DIR}/macos/Runner.xcodeproj/project.pbxproj'),
|
||||||
|
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/Flutter-Generated.xcconfig'),
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
|
// TODO(jonahwilliams): introduce configuration/planning phase to build.
|
||||||
|
// No outputs because Cocoapods is fully responsible for tracking. plus there
|
||||||
|
// is no concept of an optional output. Instead we will need a build config
|
||||||
|
// phase to conditionally add this rule so that it can be written properly.
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Target> get dependencies => const <Target>[
|
||||||
|
UnpackMacOS(),
|
||||||
|
FlutterPlugins(),
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
|
if (environment.defines[kBuildMode] == null) {
|
||||||
|
throw MissingDefineException(kBuildMode, 'debug_macos_pod_install');
|
||||||
|
}
|
||||||
|
// If there is no podfile do not perform any pods actions.
|
||||||
|
if (!environment.projectDir.childDirectory('macos')
|
||||||
|
.childFile('Podfile').existsSync()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
|
||||||
|
final FlutterProject project = FlutterProject.fromDirectory(environment.projectDir);
|
||||||
|
final String enginePath = artifacts.getArtifactPath(Artifact.flutterMacOSPodspec,
|
||||||
|
mode: buildMode, platform: TargetPlatform.darwin_x64);
|
||||||
|
|
||||||
|
await cocoaPods.processPods(
|
||||||
|
xcodeProject: project.macos,
|
||||||
|
engineDir: enginePath,
|
||||||
|
isSwift: true,
|
||||||
|
dependenciesChanged: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build all of the artifacts for a debug macOS application.
|
||||||
|
class DebugMacOSApplication extends Target {
|
||||||
|
const DebugMacOSApplication();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> build(List<File> inputFiles, Environment environment) async {
|
||||||
|
final File sourceFile = environment.buildDir.childFile('app.dill');
|
||||||
|
final File destinationFile = environment.buildDir
|
||||||
|
.childDirectory('flutter_assets')
|
||||||
|
.childFile('kernel_blob.bin');
|
||||||
|
if (!destinationFile.parent.existsSync()) {
|
||||||
|
destinationFile.parent.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
sourceFile.copySync(destinationFile.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Target> get dependencies => const <Target>[
|
||||||
|
FlutterPlugins(),
|
||||||
|
UnpackMacOS(),
|
||||||
|
KernelSnapshot(),
|
||||||
|
CopyAssets(),
|
||||||
|
DebugMacOSPodInstall(),
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get inputs => const <Source>[
|
||||||
|
Source.pattern('{BUILD_DIR}/app.dill')
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'debug_macos_application';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Source> get outputs => const <Source>[
|
||||||
|
Source.pattern('{BUILD_DIR}/flutter_assets/kernel_blob.bin'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(jonahwilliams): real AOT implementation.
|
||||||
|
class ReleaseMacOSApplication extends DebugMacOSApplication {
|
||||||
|
const ReleaseMacOSApplication();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'release_macos_application';
|
||||||
|
}
|
||||||
|
class ProfileMacOSApplication extends DebugMacOSApplication {
|
||||||
|
const ProfileMacOSApplication();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'profile_macos_application';
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,11 @@
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
import '../base/context.dart';
|
import '../base/context.dart';
|
||||||
|
import '../base/file_system.dart';
|
||||||
import '../build_system/build_system.dart';
|
import '../build_system/build_system.dart';
|
||||||
import '../build_system/targets/assets.dart';
|
import '../build_system/targets/assets.dart';
|
||||||
import '../build_system/targets/dart.dart';
|
import '../build_system/targets/dart.dart';
|
||||||
|
@ -29,6 +32,9 @@ const List<Target> _kDefaultTargets = <Target>[
|
||||||
AotElfRelease(),
|
AotElfRelease(),
|
||||||
AotAssemblyProfile(),
|
AotAssemblyProfile(),
|
||||||
AotAssemblyRelease(),
|
AotAssemblyRelease(),
|
||||||
|
DebugMacOSApplication(),
|
||||||
|
ProfileMacOSApplication(),
|
||||||
|
ReleaseMacOSApplication(),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Assemble provides a low level API to interact with the flutter tool build
|
/// Assemble provides a low level API to interact with the flutter tool build
|
||||||
|
@ -40,14 +46,14 @@ class AssembleCommand extends FlutterCommand {
|
||||||
abbr: 'd',
|
abbr: 'd',
|
||||||
help: 'Allows passing configuration to a target with --define=target=key=value.'
|
help: 'Allows passing configuration to a target with --define=target=key=value.'
|
||||||
);
|
);
|
||||||
argParser.addOption(
|
argParser.addOption('build-inputs', help: 'A file path where a newline '
|
||||||
'build-mode',
|
'separated file containing all inputs used will be written after a build.'
|
||||||
allowed: const <String>[
|
' This file is not included as a build input or output. This file is not'
|
||||||
'debug',
|
' written if the build fails for any reason.');
|
||||||
'profile',
|
argParser.addOption('build-outputs', help: 'A file path where a newline '
|
||||||
'release',
|
'separated file containing all outputs used will be written after a build.'
|
||||||
],
|
' This file is not included as a build input or output. This file is not'
|
||||||
);
|
' written if the build fails for any reason.');
|
||||||
argParser.addOption(
|
argParser.addOption(
|
||||||
'resource-pool-size',
|
'resource-pool-size',
|
||||||
help: 'The maximum number of concurrent tasks the build system will run.'
|
help: 'The maximum number of concurrent tasks the build system will run.'
|
||||||
|
@ -106,10 +112,33 @@ class AssembleCommand extends FlutterCommand {
|
||||||
printError('Target ${data.key} failed: ${data.value.exception}');
|
printError('Target ${data.key} failed: ${data.value.exception}');
|
||||||
printError('${data.value.exception}');
|
printError('${data.value.exception}');
|
||||||
}
|
}
|
||||||
throwToolExit('build failed');
|
throwToolExit('build failed.');
|
||||||
} else {
|
}
|
||||||
printStatus('build succeeded');
|
printStatus('build succeeded.');
|
||||||
|
if (argResults.wasParsed('build-inputs')) {
|
||||||
|
writeListIfChanged(result.inputFiles, argResults['build-inputs']);
|
||||||
|
}
|
||||||
|
if (argResults.wasParsed('build-outputs')) {
|
||||||
|
writeListIfChanged(result.outputFiles, argResults['build-outputs']);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
void writeListIfChanged(List<File> files, String path) {
|
||||||
|
final File file = fs.file(path);
|
||||||
|
final StringBuffer buffer = StringBuffer();
|
||||||
|
// These files are already sorted.
|
||||||
|
for (File file in files) {
|
||||||
|
buffer.writeln(file.resolveSymbolicLinksSync());
|
||||||
|
}
|
||||||
|
final String newContents = buffer.toString();
|
||||||
|
if (!file.existsSync()) {
|
||||||
|
file.writeAsStringSync(newContents);
|
||||||
|
}
|
||||||
|
final String currentContents = file.readAsStringSync();
|
||||||
|
if (currentContents != newContents) {
|
||||||
|
file.writeAsStringSync(newContents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -257,7 +257,6 @@ class AttachCommand extends FlutterCommand {
|
||||||
device,
|
device,
|
||||||
flutterProject: flutterProject,
|
flutterProject: flutterProject,
|
||||||
trackWidgetCreation: argResults['track-widget-creation'],
|
trackWidgetCreation: argResults['track-widget-creation'],
|
||||||
dillOutputPath: argResults['output-dill'],
|
|
||||||
fileSystemRoots: argResults['filesystem-root'],
|
fileSystemRoots: argResults['filesystem-root'],
|
||||||
fileSystemScheme: argResults['filesystem-scheme'],
|
fileSystemScheme: argResults['filesystem-scheme'],
|
||||||
viewFilter: argResults['isolate-filter'],
|
viewFilter: argResults['isolate-filter'],
|
||||||
|
|
|
@ -14,7 +14,7 @@ import '../project.dart';
|
||||||
import '../runner/flutter_command.dart' show FlutterCommandResult;
|
import '../runner/flutter_command.dart' show FlutterCommandResult;
|
||||||
import 'build.dart';
|
import 'build.dart';
|
||||||
|
|
||||||
/// A command to build a macos desktop target through a build shell script.
|
/// A command to build a macOS desktop target through a build shell script.
|
||||||
class BuildMacosCommand extends BuildSubCommand {
|
class BuildMacosCommand extends BuildSubCommand {
|
||||||
BuildMacosCommand() {
|
BuildMacosCommand() {
|
||||||
usesTargetOption();
|
usesTargetOption();
|
||||||
|
|
|
@ -406,7 +406,6 @@ class AppDomain extends Domain {
|
||||||
device,
|
device,
|
||||||
flutterProject: flutterProject,
|
flutterProject: flutterProject,
|
||||||
trackWidgetCreation: trackWidgetCreation,
|
trackWidgetCreation: trackWidgetCreation,
|
||||||
dillOutputPath: dillOutputPath,
|
|
||||||
viewFilter: isolateFilter,
|
viewFilter: isolateFilter,
|
||||||
target: target,
|
target: target,
|
||||||
buildMode: options.buildInfo.mode,
|
buildMode: options.buildInfo.mode,
|
||||||
|
|
|
@ -397,7 +397,6 @@ class RunCommand extends RunCommandBase {
|
||||||
device,
|
device,
|
||||||
flutterProject: flutterProject,
|
flutterProject: flutterProject,
|
||||||
trackWidgetCreation: argResults['track-widget-creation'],
|
trackWidgetCreation: argResults['track-widget-creation'],
|
||||||
dillOutputPath: argResults['output-dill'],
|
|
||||||
fileSystemRoots: argResults['filesystem-root'],
|
fileSystemRoots: argResults['filesystem-root'],
|
||||||
fileSystemScheme: argResults['filesystem-scheme'],
|
fileSystemScheme: argResults['filesystem-scheme'],
|
||||||
viewFilter: argResults['isolate-filter'],
|
viewFilter: argResults['isolate-filter'],
|
||||||
|
|
|
@ -48,13 +48,15 @@ Future<void> updateGeneratedXcodeProperties({
|
||||||
String targetOverride,
|
String targetOverride,
|
||||||
bool useMacOSConfig = false,
|
bool useMacOSConfig = false,
|
||||||
bool setSymroot = true,
|
bool setSymroot = true,
|
||||||
|
String buildDirOverride,
|
||||||
}) async {
|
}) async {
|
||||||
final List<String> xcodeBuildSettings = _xcodeBuildSettingsLines(
|
final List<String> xcodeBuildSettings = _xcodeBuildSettingsLines(
|
||||||
project: project,
|
project: project,
|
||||||
buildInfo: buildInfo,
|
buildInfo: buildInfo,
|
||||||
targetOverride: targetOverride,
|
targetOverride: targetOverride,
|
||||||
useMacOSConfig: useMacOSConfig,
|
useMacOSConfig: useMacOSConfig,
|
||||||
setSymroot: setSymroot
|
setSymroot: setSymroot,
|
||||||
|
buildDirOverride: buildDirOverride
|
||||||
);
|
);
|
||||||
|
|
||||||
_updateGeneratedXcodePropertiesFile(
|
_updateGeneratedXcodePropertiesFile(
|
||||||
|
@ -121,6 +123,7 @@ List<String> _xcodeBuildSettingsLines({
|
||||||
String targetOverride,
|
String targetOverride,
|
||||||
bool useMacOSConfig = false,
|
bool useMacOSConfig = false,
|
||||||
bool setSymroot = true,
|
bool setSymroot = true,
|
||||||
|
String buildDirOverride,
|
||||||
}) {
|
}) {
|
||||||
final List<String> xcodeBuildSettings = <String>[];
|
final List<String> xcodeBuildSettings = <String>[];
|
||||||
|
|
||||||
|
@ -135,7 +138,7 @@ List<String> _xcodeBuildSettingsLines({
|
||||||
xcodeBuildSettings.add('FLUTTER_TARGET=$targetOverride');
|
xcodeBuildSettings.add('FLUTTER_TARGET=$targetOverride');
|
||||||
|
|
||||||
// The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
|
// The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
|
||||||
xcodeBuildSettings.add('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
|
xcodeBuildSettings.add('FLUTTER_BUILD_DIR=${buildDirOverride ?? getBuildDirectory()}');
|
||||||
|
|
||||||
if (setSymroot) {
|
if (setSymroot) {
|
||||||
xcodeBuildSettings.add('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
|
xcodeBuildSettings.add('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import '../application_package.dart';
|
import '../application_package.dart';
|
||||||
import '../base/file_system.dart';
|
import '../base/file_system.dart';
|
||||||
import '../build_info.dart';
|
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../ios/plist_utils.dart' as plist;
|
import '../ios/plist_utils.dart' as plist;
|
||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
|
@ -31,18 +30,17 @@ abstract class MacOSApp extends ApplicationPackage {
|
||||||
/// which is expected to start the application and send the observatory
|
/// which is expected to start the application and send the observatory
|
||||||
/// port over stdout.
|
/// port over stdout.
|
||||||
factory MacOSApp.fromPrebuiltApp(FileSystemEntity applicationBinary) {
|
factory MacOSApp.fromPrebuiltApp(FileSystemEntity applicationBinary) {
|
||||||
final _ExecutableAndId executableAndId = _executableFromBundle(applicationBinary);
|
final ExecutableAndId executableAndId = executableFromBundle(applicationBinary);
|
||||||
final Directory applicationBundle = fs.directory(applicationBinary);
|
final Directory applicationBundle = fs.directory(applicationBinary);
|
||||||
return PrebuiltMacOSApp(
|
return PrebuiltMacOSApp(
|
||||||
bundleDir: applicationBundle,
|
bundleDir: applicationBundle,
|
||||||
bundleName: applicationBundle.path,
|
bundleName: applicationBundle.path,
|
||||||
projectBundleId: executableAndId.id,
|
executableAndId: executableAndId,
|
||||||
executable: executableAndId.executable,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Look up the executable name for a macOS application bundle.
|
/// Look up the executable name for a macOS application bundle.
|
||||||
static _ExecutableAndId _executableFromBundle(Directory applicationBundle) {
|
static ExecutableAndId executableFromBundle(Directory applicationBundle) {
|
||||||
final FileSystemEntityType entityType = fs.typeSync(applicationBundle.path);
|
final FileSystemEntityType entityType = fs.typeSync(applicationBundle.path);
|
||||||
if (entityType == FileSystemEntityType.notFound) {
|
if (entityType == FileSystemEntityType.notFound) {
|
||||||
printError('File "${applicationBundle.path}" does not exist.');
|
printError('File "${applicationBundle.path}" does not exist.');
|
||||||
|
@ -75,40 +73,28 @@ abstract class MacOSApp extends ApplicationPackage {
|
||||||
if (!fs.file(executable).existsSync()) {
|
if (!fs.file(executable).existsSync()) {
|
||||||
printError('Could not find macOS binary at $executable');
|
printError('Could not find macOS binary at $executable');
|
||||||
}
|
}
|
||||||
return _ExecutableAndId(executable, id);
|
return ExecutableAndId(executable, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get displayName => id;
|
String get displayName => id;
|
||||||
|
|
||||||
String applicationBundle(BuildMode buildMode);
|
|
||||||
|
|
||||||
String executable(BuildMode buildMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrebuiltMacOSApp extends MacOSApp {
|
class PrebuiltMacOSApp extends MacOSApp {
|
||||||
PrebuiltMacOSApp({
|
PrebuiltMacOSApp({
|
||||||
@required this.bundleDir,
|
@required this.bundleDir,
|
||||||
@required this.bundleName,
|
@required this.bundleName,
|
||||||
@required this.projectBundleId,
|
@required this.executableAndId,
|
||||||
@required String executable,
|
});
|
||||||
}) : _executable = executable,
|
|
||||||
super(projectBundleId: projectBundleId);
|
|
||||||
|
|
||||||
final Directory bundleDir;
|
final Directory bundleDir;
|
||||||
final String bundleName;
|
final String bundleName;
|
||||||
final String projectBundleId;
|
final ExecutableAndId executableAndId;
|
||||||
|
|
||||||
final String _executable;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => bundleName;
|
String get name => bundleName;
|
||||||
|
|
||||||
@override
|
String get executable => executableAndId.executable;
|
||||||
String applicationBundle(BuildMode buildMode) => bundleDir.path;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String executable(BuildMode buildMode) => _executable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BuildableMacOSApp extends MacOSApp {
|
class BuildableMacOSApp extends MacOSApp {
|
||||||
|
@ -118,35 +104,10 @@ class BuildableMacOSApp extends MacOSApp {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => 'macOS';
|
String get name => 'macOS';
|
||||||
|
|
||||||
@override
|
|
||||||
String applicationBundle(BuildMode buildMode) {
|
|
||||||
final File appBundleNameFile = project.nameFile;
|
|
||||||
if (!appBundleNameFile.existsSync()) {
|
|
||||||
printError('Unable to find app name. ${appBundleNameFile.path} does not exist');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return fs.path.join(
|
|
||||||
getMacOSBuildDirectory(),
|
|
||||||
'Build',
|
|
||||||
'Products',
|
|
||||||
buildMode == BuildMode.debug ? 'Debug' : 'Release',
|
|
||||||
appBundleNameFile.readAsStringSync().trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String executable(BuildMode buildMode) {
|
|
||||||
final String directory = applicationBundle(buildMode);
|
|
||||||
if (directory == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final _ExecutableAndId executableAndId = MacOSApp._executableFromBundle(fs.directory(directory));
|
|
||||||
return executableAndId.executable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ExecutableAndId {
|
class ExecutableAndId {
|
||||||
_ExecutableAndId(this.executable, this.id);
|
ExecutableAndId(this.executable, this.id);
|
||||||
|
|
||||||
final String executable;
|
final String executable;
|
||||||
final String id;
|
final String id;
|
||||||
|
|
|
@ -8,25 +8,34 @@ import '../base/io.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
import '../base/process_manager.dart';
|
import '../base/process_manager.dart';
|
||||||
import '../build_info.dart';
|
import '../build_info.dart';
|
||||||
|
import '../build_system/build_system.dart';
|
||||||
|
import '../build_system/targets/dart.dart';
|
||||||
import '../convert.dart';
|
import '../convert.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../ios/xcodeproj.dart';
|
import '../ios/xcodeproj.dart';
|
||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
import '../reporting/reporting.dart';
|
import '../reporting/reporting.dart';
|
||||||
|
import 'application_package.dart';
|
||||||
|
|
||||||
import 'cocoapod_utils.dart';
|
/// Builds the macOS project through xcodebuild and returns the app bundle.
|
||||||
|
Future<PrebuiltMacOSApp> buildMacOS({
|
||||||
/// Builds the macOS project through xcodebuild.
|
|
||||||
// TODO(jonahwilliams): refactor to share code with the existing iOS code.
|
|
||||||
Future<void> buildMacOS({
|
|
||||||
FlutterProject flutterProject,
|
FlutterProject flutterProject,
|
||||||
BuildInfo buildInfo,
|
BuildInfo buildInfo,
|
||||||
String targetOverride,
|
String targetOverride = 'lib/main.dart',
|
||||||
}) async {
|
}) async {
|
||||||
final Directory flutterBuildDir = fs.directory(getMacOSBuildDirectory());
|
// Create the environment used to process the build. This needs to match what
|
||||||
if (!flutterBuildDir.existsSync()) {
|
// is provided in bin/macos_build_flutter_assets.sh otherwise the directories
|
||||||
flutterBuildDir.createSync(recursive: true);
|
// will be different.
|
||||||
}
|
final Environment environment = Environment(
|
||||||
|
projectDir: flutterProject.directory,
|
||||||
|
buildDir: flutterProject.dartTool.childDirectory('flutter_build'),
|
||||||
|
defines: <String, String>{
|
||||||
|
kBuildMode: buildInfo.isDebug == true ? 'debug' : 'release',
|
||||||
|
kTargetPlatform: 'darwin-x64',
|
||||||
|
kTargetFile: fs.file(targetOverride).absolute.path
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Write configuration to an xconfig file in a standard location.
|
// Write configuration to an xconfig file in a standard location.
|
||||||
await updateGeneratedXcodeProperties(
|
await updateGeneratedXcodeProperties(
|
||||||
project: flutterProject,
|
project: flutterProject,
|
||||||
|
@ -34,27 +43,34 @@ Future<void> buildMacOS({
|
||||||
targetOverride: targetOverride,
|
targetOverride: targetOverride,
|
||||||
useMacOSConfig: true,
|
useMacOSConfig: true,
|
||||||
setSymroot: false,
|
setSymroot: false,
|
||||||
|
buildDirOverride: environment.buildDir.path,
|
||||||
);
|
);
|
||||||
await processPodsIfNeeded(flutterProject.macos, getMacOSBuildDirectory(), buildInfo.mode);
|
// If the xcfilelists do not exist, create empty version.
|
||||||
|
if (!flutterProject.macos.inputFileList.existsSync()) {
|
||||||
|
flutterProject.macos.inputFileList.createSync(recursive: true);
|
||||||
|
}
|
||||||
|
if (!flutterProject.macos.outputFileList.existsSync()) {
|
||||||
|
flutterProject.macos.outputFileList.createSync(recursive: true);
|
||||||
|
}
|
||||||
// Set debug or release mode.
|
// Set debug or release mode.
|
||||||
String config = 'Debug';
|
String config = 'Debug';
|
||||||
if (buildInfo.isRelease) {
|
if (buildInfo.isRelease ?? false) {
|
||||||
config = 'Release';
|
config = 'Release';
|
||||||
}
|
}
|
||||||
// Run build script provided by application.
|
// Invoke Xcode with correct configuration.
|
||||||
final Stopwatch sw = Stopwatch()..start();
|
final Stopwatch sw = Stopwatch()..start();
|
||||||
final Process process = await processManager.start(<String>[
|
final List<String> command = <String>[
|
||||||
'/usr/bin/env',
|
'/usr/bin/env',
|
||||||
'xcrun',
|
'xcrun',
|
||||||
'xcodebuild',
|
'xcodebuild',
|
||||||
'-workspace', flutterProject.macos.xcodeWorkspace.path,
|
'-workspace', flutterProject.macos.xcodeWorkspace.path,
|
||||||
'-configuration', '$config',
|
'-configuration', config,
|
||||||
'-scheme', 'Runner',
|
'-scheme', 'Runner',
|
||||||
'-derivedDataPath', flutterBuildDir.absolute.path,
|
'-derivedDataPath', environment.buildDir.path,
|
||||||
'OBJROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
|
'OBJROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Intermediates.noindex')}',
|
||||||
'SYMROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
|
'SYMROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Products')}',
|
||||||
], runInShell: true);
|
];
|
||||||
|
final Process process = await processManager.start(command);
|
||||||
final Status status = logger.startProgress(
|
final Status status = logger.startProgress(
|
||||||
'Building macOS application...',
|
'Building macOS application...',
|
||||||
timeout: null,
|
timeout: null,
|
||||||
|
@ -77,4 +93,13 @@ Future<void> buildMacOS({
|
||||||
throwToolExit('Build process failed');
|
throwToolExit('Build process failed');
|
||||||
}
|
}
|
||||||
flutterUsage.sendTiming('build', 'xcode-macos', Duration(milliseconds: sw.elapsedMilliseconds));
|
flutterUsage.sendTiming('build', 'xcode-macos', Duration(milliseconds: sw.elapsedMilliseconds));
|
||||||
|
final File appBundleNameFile = flutterProject.macos.nameFile;
|
||||||
|
final Directory bundleDir = fs.directory(fs.path.join(
|
||||||
|
environment.buildDir.path,
|
||||||
|
'Build',
|
||||||
|
'Products',
|
||||||
|
buildInfo.mode == BuildMode.debug ? 'Debug' : 'Release',
|
||||||
|
appBundleNameFile.readAsStringSync().trim(),
|
||||||
|
));
|
||||||
|
return MacOSApp.fromPrebuiltApp(bundleDir);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,10 +80,13 @@ class MacOSDevice extends Device {
|
||||||
bool usesTerminalUi = true,
|
bool usesTerminalUi = true,
|
||||||
bool ipv6 = false,
|
bool ipv6 = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
Cache.releaseLockEarly();
|
||||||
// Stop any running applications with the same executable.
|
// Stop any running applications with the same executable.
|
||||||
if (!prebuiltApplication) {
|
PrebuiltMacOSApp prebuiltMacOSApp;
|
||||||
Cache.releaseLockEarly();
|
if (prebuiltApplication) {
|
||||||
await buildMacOS(
|
prebuiltMacOSApp = package;
|
||||||
|
} else {
|
||||||
|
prebuiltMacOSApp = await buildMacOS(
|
||||||
flutterProject: FlutterProject.current(),
|
flutterProject: FlutterProject.current(),
|
||||||
buildInfo: debuggingOptions?.buildInfo,
|
buildInfo: debuggingOptions?.buildInfo,
|
||||||
targetOverride: mainPath,
|
targetOverride: mainPath,
|
||||||
|
@ -91,8 +94,7 @@ class MacOSDevice extends Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the executable is locatable.
|
// Ensure that the executable is locatable.
|
||||||
final String executable = package.executable(debuggingOptions?.buildInfo?.mode);
|
if (prebuiltMacOSApp == null) {
|
||||||
if (executable == null) {
|
|
||||||
printError('Unable to find executable to run');
|
printError('Unable to find executable to run');
|
||||||
return LaunchResult.failed();
|
return LaunchResult.failed();
|
||||||
}
|
}
|
||||||
|
@ -100,7 +102,7 @@ class MacOSDevice extends Device {
|
||||||
// Make sure to call stop app after we've built.
|
// Make sure to call stop app after we've built.
|
||||||
await stopApp(package);
|
await stopApp(package);
|
||||||
final Process process = await processManager.start(<String>[
|
final Process process = await processManager.start(<String>[
|
||||||
executable
|
prebuiltMacOSApp.executable,
|
||||||
]);
|
]);
|
||||||
if (debuggingOptions?.buildInfo?.isRelease == true) {
|
if (debuggingOptions?.buildInfo?.isRelease == true) {
|
||||||
return LaunchResult.succeeded();
|
return LaunchResult.succeeded();
|
||||||
|
@ -111,7 +113,7 @@ class MacOSDevice extends Device {
|
||||||
final Uri observatoryUri = await observatoryDiscovery.uri;
|
final Uri observatoryUri = await observatoryDiscovery.uri;
|
||||||
// Bring app to foreground.
|
// Bring app to foreground.
|
||||||
await processManager.run(<String>[
|
await processManager.run(<String>[
|
||||||
'open', package.applicationBundle(debuggingOptions?.buildInfo?.mode),
|
'open', prebuiltMacOSApp.bundleName,
|
||||||
]);
|
]);
|
||||||
return LaunchResult.succeeded(observatoryUri: observatoryUri);
|
return LaunchResult.succeeded(observatoryUri: observatoryUri);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -125,9 +127,8 @@ class MacOSDevice extends Device {
|
||||||
// TODO(jonahwilliams): implement using process manager.
|
// TODO(jonahwilliams): implement using process manager.
|
||||||
// currently we rely on killing the isolate taking down the application.
|
// currently we rely on killing the isolate taking down the application.
|
||||||
@override
|
@override
|
||||||
Future<bool> stopApp(covariant MacOSApp app) async {
|
Future<bool> stopApp(covariant PrebuiltMacOSApp app) async {
|
||||||
// Assume debug for now.
|
return killProcess(app.executable);
|
||||||
return killProcess(app.executable(BuildMode.debug));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -664,6 +664,14 @@ class MacOSProject implements XcodeBasedProject {
|
||||||
/// checked in should live here.
|
/// checked in should live here.
|
||||||
Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral');
|
Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral');
|
||||||
|
|
||||||
|
/// The xcfilelist used to track the inputs for the Flutter script phase in
|
||||||
|
/// the Xcode build.
|
||||||
|
File get inputFileList => ephemeralDirectory.childFile('FlutterInputs.xcfilelist');
|
||||||
|
|
||||||
|
/// The xcfilelist used to track the outputs for the Flutter script phase in
|
||||||
|
/// the Xcode build.
|
||||||
|
File get outputFileList => ephemeralDirectory.childFile('FlutterOutputs.xcfilelist');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
File get generatedXcodePropertiesFile => ephemeralDirectory.childFile('Flutter-Generated.xcconfig');
|
File get generatedXcodePropertiesFile => ephemeralDirectory.childFile('Flutter-Generated.xcconfig');
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ class FlutterDevice {
|
||||||
FlutterDevice(
|
FlutterDevice(
|
||||||
this.device, {
|
this.device, {
|
||||||
@required this.trackWidgetCreation,
|
@required this.trackWidgetCreation,
|
||||||
this.dillOutputPath,
|
|
||||||
this.fileSystemRoots,
|
this.fileSystemRoots,
|
||||||
this.fileSystemScheme,
|
this.fileSystemScheme,
|
||||||
this.viewFilter,
|
this.viewFilter,
|
||||||
|
@ -56,7 +55,6 @@ class FlutterDevice {
|
||||||
@required bool trackWidgetCreation,
|
@required bool trackWidgetCreation,
|
||||||
@required String target,
|
@required String target,
|
||||||
@required BuildMode buildMode,
|
@required BuildMode buildMode,
|
||||||
String dillOutputPath,
|
|
||||||
List<String> fileSystemRoots,
|
List<String> fileSystemRoots,
|
||||||
String fileSystemScheme,
|
String fileSystemScheme,
|
||||||
String viewFilter,
|
String viewFilter,
|
||||||
|
@ -82,7 +80,6 @@ class FlutterDevice {
|
||||||
return FlutterDevice(
|
return FlutterDevice(
|
||||||
device,
|
device,
|
||||||
trackWidgetCreation: trackWidgetCreation,
|
trackWidgetCreation: trackWidgetCreation,
|
||||||
dillOutputPath: dillOutputPath,
|
|
||||||
fileSystemRoots: fileSystemRoots,
|
fileSystemRoots: fileSystemRoots,
|
||||||
fileSystemScheme:fileSystemScheme,
|
fileSystemScheme:fileSystemScheme,
|
||||||
viewFilter: viewFilter,
|
viewFilter: viewFilter,
|
||||||
|
@ -99,7 +96,6 @@ class FlutterDevice {
|
||||||
List<VMService> vmServices;
|
List<VMService> vmServices;
|
||||||
DevFS devFS;
|
DevFS devFS;
|
||||||
ApplicationPackage package;
|
ApplicationPackage package;
|
||||||
String dillOutputPath;
|
|
||||||
List<String> fileSystemRoots;
|
List<String> fileSystemRoots;
|
||||||
String fileSystemScheme;
|
String fileSystemScheme;
|
||||||
StreamSubscription<String> _loggingSubscription;
|
StreamSubscription<String> _loggingSubscription;
|
||||||
|
@ -476,6 +472,7 @@ class FlutterDevice {
|
||||||
bool fullRestart = false,
|
bool fullRestart = false,
|
||||||
String projectRootPath,
|
String projectRootPath,
|
||||||
String pathToReload,
|
String pathToReload,
|
||||||
|
@required String dillOutputPath,
|
||||||
@required List<Uri> invalidatedFiles,
|
@required List<Uri> invalidatedFiles,
|
||||||
}) async {
|
}) async {
|
||||||
final Status devFSStatus = logger.startProgress(
|
final Status devFSStatus = logger.startProgress(
|
||||||
|
@ -527,12 +524,25 @@ abstract class ResidentRunner {
|
||||||
this.usesTerminalUi = true,
|
this.usesTerminalUi = true,
|
||||||
this.stayResident = true,
|
this.stayResident = true,
|
||||||
this.hotMode = true,
|
this.hotMode = true,
|
||||||
|
this.dillOutputPath,
|
||||||
}) {
|
}) {
|
||||||
_mainPath = findMainDartFile(target);
|
_mainPath = findMainDartFile(target);
|
||||||
_projectRootPath = projectRootPath ?? fs.currentDirectory.path;
|
_projectRootPath = projectRootPath ?? fs.currentDirectory.path;
|
||||||
_packagesFilePath =
|
_packagesFilePath =
|
||||||
packagesFilePath ?? fs.path.absolute(PackageMap.globalPackagesPath);
|
packagesFilePath ?? fs.path.absolute(PackageMap.globalPackagesPath);
|
||||||
_assetBundle = AssetBundleFactory.instance.createBundle();
|
_assetBundle = AssetBundleFactory.instance.createBundle();
|
||||||
|
// TODO(jonahwilliams): this is transitionary logic to allow us to support
|
||||||
|
// platforms that are not yet using flutter assemble. In the "new world",
|
||||||
|
// builds are isolated based on a number of factors. Thus, we cannot assume
|
||||||
|
// that a debug build will create the expected `build/app.dill` file. For
|
||||||
|
// now, I'm working around this by just creating it if it is missing here.
|
||||||
|
// In the future, once build & run are more strongly separated, the build
|
||||||
|
// environment will be plumbed through so that it all comes from a single
|
||||||
|
// source of truth, the [Environment].
|
||||||
|
final File dillOutput = fs.file(dillOutputPath ?? fs.path.join('build', 'app.dill'));
|
||||||
|
if (!dillOutput.existsSync()) {
|
||||||
|
dillOutput.createSync(recursive: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<FlutterDevice> flutterDevices;
|
final List<FlutterDevice> flutterDevices;
|
||||||
|
@ -542,6 +552,7 @@ abstract class ResidentRunner {
|
||||||
final bool stayResident;
|
final bool stayResident;
|
||||||
final bool ipv6;
|
final bool ipv6;
|
||||||
final Completer<int> _finished = Completer<int>();
|
final Completer<int> _finished = Completer<int>();
|
||||||
|
final String dillOutputPath;
|
||||||
bool _exited = false;
|
bool _exited = false;
|
||||||
bool hotMode ;
|
bool hotMode ;
|
||||||
String _packagesFilePath;
|
String _packagesFilePath;
|
||||||
|
|
|
@ -63,7 +63,7 @@ class HotRunner extends ResidentRunner {
|
||||||
this.hostIsIde = false,
|
this.hostIsIde = false,
|
||||||
String projectRootPath,
|
String projectRootPath,
|
||||||
String packagesFilePath,
|
String packagesFilePath,
|
||||||
this.dillOutputPath,
|
String dillOutputPath,
|
||||||
bool stayResident = true,
|
bool stayResident = true,
|
||||||
bool ipv6 = false,
|
bool ipv6 = false,
|
||||||
}) : super(devices,
|
}) : super(devices,
|
||||||
|
@ -74,13 +74,13 @@ class HotRunner extends ResidentRunner {
|
||||||
packagesFilePath: packagesFilePath,
|
packagesFilePath: packagesFilePath,
|
||||||
stayResident: stayResident,
|
stayResident: stayResident,
|
||||||
hotMode: true,
|
hotMode: true,
|
||||||
|
dillOutputPath: dillOutputPath,
|
||||||
ipv6: ipv6);
|
ipv6: ipv6);
|
||||||
|
|
||||||
final bool benchmarkMode;
|
final bool benchmarkMode;
|
||||||
final File applicationBinary;
|
final File applicationBinary;
|
||||||
final bool hostIsIde;
|
final bool hostIsIde;
|
||||||
bool _didAttach = false;
|
bool _didAttach = false;
|
||||||
final String dillOutputPath;
|
|
||||||
|
|
||||||
final Map<String, List<int>> benchmarkData = <String, List<int>>{};
|
final Map<String, List<int>> benchmarkData = <String, List<int>>{};
|
||||||
// The initial launch is from a snapshot.
|
// The initial launch is from a snapshot.
|
||||||
|
@ -304,6 +304,7 @@ class HotRunner extends ResidentRunner {
|
||||||
projectRootPath: projectRootPath,
|
projectRootPath: projectRootPath,
|
||||||
pathToReload: getReloadPath(fullRestart: fullRestart),
|
pathToReload: getReloadPath(fullRestart: fullRestart),
|
||||||
invalidatedFiles: invalidatedFiles,
|
invalidatedFiles: invalidatedFiles,
|
||||||
|
dillOutputPath: dillOutputPath,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
|
|
|
@ -138,6 +138,14 @@ void main() {
|
||||||
expect(stampContents['inputs'], <Object>['/foo.dart']);
|
expect(stampContents['inputs'], <Object>['/foo.dart']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
test('Creates a BuildResult with inputs and outputs', () => testbed.run(() async {
|
||||||
|
final BuildResult result = await buildSystem.build(fooTarget, environment);
|
||||||
|
|
||||||
|
expect(result.inputFiles.single.path, fs.path.absolute('foo.dart'));
|
||||||
|
expect(result.outputFiles.single.path,
|
||||||
|
fs.path.absolute(fs.path.join(environment.buildDir.path, 'out')));
|
||||||
|
}));
|
||||||
|
|
||||||
test('Does not re-invoke build if stamp is valid', () => testbed.run(() async {
|
test('Does not re-invoke build if stamp is valid', () => testbed.run(() async {
|
||||||
await buildSystem.build(fooTarget, environment);
|
await buildSystem.build(fooTarget, environment);
|
||||||
await buildSystem.build(fooTarget, environment);
|
await buildSystem.build(fooTarget, environment);
|
||||||
|
|
|
@ -135,6 +135,14 @@ void main() {
|
||||||
|
|
||||||
expect(() => invalidBase.accept(visitor), throwsA(isInstanceOf<InvalidPatternException>()));
|
expect(() => invalidBase.accept(visitor), throwsA(isInstanceOf<InvalidPatternException>()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
test('can substitute optional files', () => testbed.run(() {
|
||||||
|
const Source missingSource = Source.pattern('{PROJECT_DIR}/foo', optional: true);
|
||||||
|
|
||||||
|
expect(fs.file('foo').existsSync(), false);
|
||||||
|
missingSource.accept(visitor);
|
||||||
|
expect(visitor.sources, isEmpty);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestBehavior extends SourceBehavior {
|
class TestBehavior extends SourceBehavior {
|
||||||
|
|
|
@ -65,4 +65,15 @@ flutter:
|
||||||
// See https://github.com/flutter/flutter/issues/35293
|
// See https://github.com/flutter/flutter/issues/35293
|
||||||
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), false);
|
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), false);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
test('FlutterPlugins updates required files as needed', () => testbed.run(() async {
|
||||||
|
fs.file('pubspec.yaml')
|
||||||
|
..writeAsStringSync('name: foo\ndependencies:\n foo: any\n');
|
||||||
|
|
||||||
|
await const FlutterPlugins().build(<File>[], Environment(
|
||||||
|
projectDir: fs.currentDirectory,
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(fs.file('.flutter-plugins').existsSync(), true);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,11 @@ import 'package:flutter_tools/src/base/io.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/base/process_manager.dart';
|
import 'package:flutter_tools/src/base/process_manager.dart';
|
||||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||||
|
import 'package:flutter_tools/src/build_system/exceptions.dart';
|
||||||
|
import 'package:flutter_tools/src/build_system/targets/dart.dart';
|
||||||
import 'package:flutter_tools/src/build_system/targets/macos.dart';
|
import 'package:flutter_tools/src/build_system/targets/macos.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
|
import 'package:flutter_tools/src/macos/cocoapods.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:process/process.dart';
|
import 'package:process/process.dart';
|
||||||
|
|
||||||
|
@ -17,7 +20,6 @@ import '../../../src/testbed.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Testbed testbed;
|
Testbed testbed;
|
||||||
const BuildSystem buildSystem = BuildSystem();
|
|
||||||
Environment environment;
|
Environment environment;
|
||||||
MockPlatform mockPlatform;
|
MockPlatform mockPlatform;
|
||||||
|
|
||||||
|
@ -31,6 +33,7 @@ void main() {
|
||||||
when(mockPlatform.isWindows).thenReturn(false);
|
when(mockPlatform.isWindows).thenReturn(false);
|
||||||
when(mockPlatform.isMacOS).thenReturn(true);
|
when(mockPlatform.isMacOS).thenReturn(true);
|
||||||
when(mockPlatform.isLinux).thenReturn(false);
|
when(mockPlatform.isLinux).thenReturn(false);
|
||||||
|
when(mockPlatform.environment).thenReturn(const <String, String>{});
|
||||||
testbed = Testbed(setup: () {
|
testbed = Testbed(setup: () {
|
||||||
environment = Environment(
|
environment = Environment(
|
||||||
projectDir: fs.currentDirectory,
|
projectDir: fs.currentDirectory,
|
||||||
|
@ -79,28 +82,117 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Copies files to correct cache directory', () => testbed.run(() async {
|
test('Copies files to correct cache directory', () => testbed.run(() async {
|
||||||
await buildSystem.build(const UnpackMacOS(), environment);
|
await const UnpackMacOS().build(<File>[], environment);
|
||||||
|
|
||||||
expect(fs.directory('macos/Flutter/FlutterMacOS.framework').existsSync(), true);
|
expect(fs.directory('macos/Flutter/ephemeral/FlutterMacOS.framework').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/FlutterMacOS').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/FlutterMacOS').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEOpenGLContextHandling.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FLEOpenGLContextHandling.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEReshapeListener.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FLEReshapeListener.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEView.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FLEView.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FLEViewController.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FLEViewController.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterChannels.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterChannels.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterCodecs.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterCodecs.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterMacOS.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterMacOS.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Modules/module.modulemap').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Modules/module.modulemap').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Resources/icudtl.dat').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Resources/icudtl.dat').existsSync(), true);
|
||||||
expect(fs.file('macos/Flutter/FlutterMacOS.framework/Resources/info.plist').existsSync(), true);
|
expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Resources/info.plist').existsSync(), true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
test('debug macOS application copies kernel blob', () => testbed.run(() async {
|
||||||
|
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
|
||||||
|
final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin');
|
||||||
|
fs.file(inputKernel)
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('testing');
|
||||||
|
|
||||||
|
await const DebugMacOSApplication().build(<File>[], environment);
|
||||||
|
|
||||||
|
expect(fs.file(outputKernel).readAsStringSync(), 'testing');
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('profile macOS application copies kernel blob', () => testbed.run(() async {
|
||||||
|
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
|
||||||
|
final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin');
|
||||||
|
fs.file(inputKernel)
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('testing');
|
||||||
|
|
||||||
|
await const ProfileMacOSApplication().build(<File>[], environment);
|
||||||
|
|
||||||
|
expect(fs.file(outputKernel).readAsStringSync(), 'testing');
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('release macOS application copies kernel blob', () => testbed.run(() async {
|
||||||
|
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
|
||||||
|
final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin');
|
||||||
|
fs.file(inputKernel)
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('testing');
|
||||||
|
|
||||||
|
await const ReleaseMacOSApplication().build(<File>[], environment);
|
||||||
|
|
||||||
|
expect(fs.file(outputKernel).readAsStringSync(), 'testing');
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Changing target names will require a corresponding update in flutter_tools/bin/macos_build_flutter_assets.sh.
|
||||||
|
test('Target names match those expected by bin scripts', () => testbed.run(() async {
|
||||||
|
expect(const DebugMacOSApplication().name, 'debug_macos_application');
|
||||||
|
expect(const ProfileMacOSApplication().name, 'profile_macos_application');
|
||||||
|
expect(const ReleaseMacOSApplication().name, 'release_macos_application');
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
test('DebugMacOSPodInstall throws if missing build mode', () => testbed.run(() async {
|
||||||
|
expect(() => const DebugMacOSPodInstall().build(<File>[], environment),
|
||||||
|
throwsA(isInstanceOf<MissingDefineException>()));
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('DebugMacOSPodInstall skips if podfile does not exist', () => testbed.run(() async {
|
||||||
|
await const DebugMacOSPodInstall().build(<File>[], Environment(
|
||||||
|
projectDir: fs.currentDirectory,
|
||||||
|
defines: <String, String>{
|
||||||
|
kBuildMode: 'debug'
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
verifyNever(cocoaPods.processPods(
|
||||||
|
xcodeProject: anyNamed('xcodeProject'),
|
||||||
|
engineDir: anyNamed('engineDir'),
|
||||||
|
isSwift: true,
|
||||||
|
dependenciesChanged: true));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
CocoaPods: () => MockCocoaPods(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('DebugMacOSPodInstall invokes processPods with podfile', () => testbed.run(() async {
|
||||||
|
fs.file(fs.path.join('macos', 'Podfile')).createSync(recursive: true);
|
||||||
|
await const DebugMacOSPodInstall().build(<File>[], Environment(
|
||||||
|
projectDir: fs.currentDirectory,
|
||||||
|
defines: <String, String>{
|
||||||
|
kBuildMode: 'debug'
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
verify(cocoaPods.processPods(
|
||||||
|
xcodeProject: anyNamed('xcodeProject'),
|
||||||
|
engineDir: anyNamed('engineDir'),
|
||||||
|
isSwift: true,
|
||||||
|
dependenciesChanged: true)).called(1);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
CocoaPods: () => MockCocoaPods(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('b', () => testbed.run(() async {
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockPlatform extends Mock implements Platform {}
|
class MockPlatform extends Mock implements Platform {}
|
||||||
|
class MockCocoaPods extends Mock implements CocoaPods {}
|
||||||
class MockProcessManager extends Mock implements ProcessManager {}
|
class MockProcessManager extends Mock implements ProcessManager {}
|
||||||
class FakeProcessResult implements ProcessResult {
|
class FakeProcessResult implements ProcessResult {
|
||||||
@override
|
@override
|
||||||
|
@ -115,3 +207,5 @@ class FakeProcessResult implements ProcessResult {
|
||||||
@override
|
@override
|
||||||
String stdout = '';
|
String stdout = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
|
@ -31,13 +32,54 @@ void main() {
|
||||||
test('Can run a build', () => testbed.run(() async {
|
test('Can run a build', () => testbed.run(() async {
|
||||||
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
|
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
|
||||||
.thenAnswer((Invocation invocation) async {
|
.thenAnswer((Invocation invocation) async {
|
||||||
return BuildResult(true, const <String, ExceptionMeasurement>{}, const <String, PerformanceMeasurement>{});
|
return BuildResult(success: true);
|
||||||
});
|
});
|
||||||
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
|
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
|
||||||
await commandRunner.run(<String>['assemble', 'unpack_macos']);
|
await commandRunner.run(<String>['assemble', 'unpack_macos']);
|
||||||
final BufferLogger bufferLogger = logger;
|
final BufferLogger bufferLogger = logger;
|
||||||
|
|
||||||
expect(bufferLogger.statusText.trim(), 'build succeeded');
|
expect(bufferLogger.statusText.trim(), 'build succeeded.');
|
||||||
|
}));
|
||||||
|
|
||||||
|
test('Only writes input and output files when the values change', () => testbed.run(() async {
|
||||||
|
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
|
||||||
|
.thenAnswer((Invocation invocation) async {
|
||||||
|
return BuildResult(
|
||||||
|
success: true,
|
||||||
|
inputFiles: <File>[fs.file('foo')..createSync()],
|
||||||
|
outputFiles: <File>[fs.file('bar')..createSync()],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
|
||||||
|
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'unpack_macos']);
|
||||||
|
|
||||||
|
final File inputs = fs.file('inputs');
|
||||||
|
final File outputs = fs.file('outputs');
|
||||||
|
expect(inputs.readAsStringSync(), contains('foo'));
|
||||||
|
expect(outputs.readAsStringSync(), contains('bar'));
|
||||||
|
|
||||||
|
final DateTime theDistantPast = DateTime(1991, 8, 23);
|
||||||
|
inputs.setLastModifiedSync(theDistantPast);
|
||||||
|
outputs.setLastModifiedSync(theDistantPast);
|
||||||
|
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'unpack_macos']);
|
||||||
|
|
||||||
|
expect(inputs.lastModifiedSync(), theDistantPast);
|
||||||
|
expect(outputs.lastModifiedSync(), theDistantPast);
|
||||||
|
|
||||||
|
|
||||||
|
when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
|
||||||
|
.thenAnswer((Invocation invocation) async {
|
||||||
|
return BuildResult(
|
||||||
|
success: true,
|
||||||
|
inputFiles: <File>[fs.file('foo'), fs.file('fizz')..createSync()],
|
||||||
|
outputFiles: <File>[fs.file('bar')]);
|
||||||
|
});
|
||||||
|
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'unpack_macos']);
|
||||||
|
|
||||||
|
expect(inputs.readAsStringSync(), contains('foo'));
|
||||||
|
expect(inputs.readAsStringSync(), contains('fizz'));
|
||||||
|
expect(inputs.lastModifiedSync(), isNot(theDistantPast));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,6 @@ void main() {
|
||||||
// output dill, filesystem scheme, and filesystem root.
|
// output dill, filesystem scheme, and filesystem root.
|
||||||
final FlutterDevice flutterDevice = flutterDevices.first;
|
final FlutterDevice flutterDevice = flutterDevices.first;
|
||||||
|
|
||||||
expect(flutterDevice.dillOutputPath, outputDill);
|
|
||||||
expect(flutterDevice.fileSystemScheme, filesystemScheme);
|
expect(flutterDevice.fileSystemScheme, filesystemScheme);
|
||||||
expect(flutterDevice.fileSystemRoots, const <String>[filesystemRoot]);
|
expect(flutterDevice.fileSystemRoots, const <String>[filesystemRoot]);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
|
|
|
@ -8,7 +8,8 @@ import 'package:flutter_tools/src/base/common.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/io.dart';
|
import 'package:flutter_tools/src/base/io.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/build_info.dart';
|
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||||
|
import 'package:flutter_tools/src/build_system/targets/dart.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/commands/build.dart';
|
import 'package:flutter_tools/src/commands/build.dart';
|
||||||
import 'package:flutter_tools/src/features.dart';
|
import 'package:flutter_tools/src/features.dart';
|
||||||
|
@ -86,25 +87,35 @@ void main() {
|
||||||
fs.file('.packages').createSync();
|
fs.file('.packages').createSync();
|
||||||
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
|
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
|
||||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(fs.currentDirectory);
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(fs.currentDirectory);
|
||||||
final Directory flutterBuildDir = fs.directory(getMacOSBuildDirectory());
|
final Environment environment = Environment(
|
||||||
|
projectDir: flutterProject.directory,
|
||||||
|
buildDir: flutterProject.dartTool.childDirectory('flutter_build'),
|
||||||
|
defines: <String, String>{
|
||||||
|
kBuildMode: 'release',
|
||||||
|
kTargetFile: fs.path.absolute(fs.path.join('lib', 'main.dart')),
|
||||||
|
kTargetPlatform: 'darwin-x64',
|
||||||
|
}
|
||||||
|
);
|
||||||
when(mockProcessManager.start(<String>[
|
when(mockProcessManager.start(<String>[
|
||||||
'/usr/bin/env',
|
'/usr/bin/env',
|
||||||
'xcrun',
|
'xcrun',
|
||||||
'xcodebuild',
|
'xcodebuild',
|
||||||
'-workspace', flutterProject.macos.xcodeWorkspace.path,
|
'-workspace', flutterProject.macos.xcodeWorkspace.path,
|
||||||
'-configuration', 'Debug',
|
'-configuration', 'Release',
|
||||||
'-scheme', 'Runner',
|
'-scheme', 'Runner',
|
||||||
'-derivedDataPath', flutterBuildDir.absolute.path,
|
'-derivedDataPath', environment.buildDir.path,
|
||||||
'OBJROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
|
'OBJROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Intermediates.noindex')}',
|
||||||
'SYMROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
|
'SYMROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Products')}',
|
||||||
], runInShell: true)).thenAnswer((Invocation invocation) async {
|
])).thenAnswer((Invocation invocation) async {
|
||||||
|
fs.file(fs.path.join('macos', 'Flutter', 'ephemeral', '.app_filename'))
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('example.app');
|
||||||
return mockProcess;
|
return mockProcess;
|
||||||
});
|
});
|
||||||
|
|
||||||
await createTestCommandRunner(command).run(
|
expect(createTestCommandRunner(command).run(
|
||||||
const <String>['build', 'macos']
|
const <String>['build', 'macos', '--release']
|
||||||
);
|
), throwsA(isInstanceOf<AssertionError>()));
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FileSystem: () => memoryFilesystem,
|
FileSystem: () => memoryFilesystem,
|
||||||
ProcessManager: () => mockProcessManager,
|
ProcessManager: () => mockProcessManager,
|
||||||
|
|
|
@ -36,7 +36,6 @@ void main() {
|
||||||
|
|
||||||
testUsingContext('defaults', () async {
|
testUsingContext('defaults', () async {
|
||||||
final MockMacOSApp mockMacOSApp = MockMacOSApp();
|
final MockMacOSApp mockMacOSApp = MockMacOSApp();
|
||||||
when(mockMacOSApp.executable(any)).thenReturn('foo');
|
|
||||||
expect(await device.targetPlatform, TargetPlatform.darwin_x64);
|
expect(await device.targetPlatform, TargetPlatform.darwin_x64);
|
||||||
expect(device.name, 'macOS');
|
expect(device.name, 'macOS');
|
||||||
expect(await device.installApp(mockMacOSApp), true);
|
expect(await device.installApp(mockMacOSApp), true);
|
||||||
|
@ -54,7 +53,7 @@ void main() {
|
||||||
tester 17193 0.0 0.2 4791128 37820 ?? S 2:27PM 0:00.09 /Applications/foo
|
tester 17193 0.0 0.2 4791128 37820 ?? S 2:27PM 0:00.09 /Applications/foo
|
||||||
''';
|
''';
|
||||||
final MockMacOSApp mockMacOSApp = MockMacOSApp();
|
final MockMacOSApp mockMacOSApp = MockMacOSApp();
|
||||||
when(mockMacOSApp.executable(any)).thenReturn('/Applications/foo');
|
when(mockMacOSApp.executable).thenReturn('tester');
|
||||||
when(mockProcessManager.run(<String>['ps', 'aux'])).thenAnswer((Invocation invocation) async {
|
when(mockProcessManager.run(<String>['ps', 'aux'])).thenAnswer((Invocation invocation) async {
|
||||||
return ProcessResult(1, 0, psOut, '');
|
return ProcessResult(1, 0, psOut, '');
|
||||||
});
|
});
|
||||||
|
@ -68,27 +67,35 @@ tester 17193 0.0 0.2 4791128 37820 ?? S 2:27PM 0:00.09 /Applica
|
||||||
});
|
});
|
||||||
|
|
||||||
group('startApp', () {
|
group('startApp', () {
|
||||||
final MockMacOSApp macOSApp = MockMacOSApp();
|
MockMacOSApp macOSApp;
|
||||||
final MockFileSystem mockFileSystem = MockFileSystem();
|
MockFileSystem mockFileSystem;
|
||||||
final MockProcessManager mockProcessManager = MockProcessManager();
|
MockProcessManager mockProcessManager;
|
||||||
final MockFile mockFile = MockFile();
|
MockFile mockFile;
|
||||||
when(macOSApp.executable(any)).thenReturn('test');
|
|
||||||
when(mockFileSystem.file('test')).thenReturn(mockFile);
|
|
||||||
when(mockFile.existsSync()).thenReturn(true);
|
setUp(() {
|
||||||
when(mockProcessManager.start(<String>['test'])).thenAnswer((Invocation invocation) async {
|
macOSApp = MockMacOSApp();
|
||||||
return FakeProcess(
|
mockFileSystem = MockFileSystem();
|
||||||
exitCode: Completer<int>().future,
|
mockProcessManager = MockProcessManager();
|
||||||
stdout: Stream<List<int>>.fromIterable(<List<int>>[
|
mockFile = MockFile();
|
||||||
utf8.encode('Observatory listening on http://127.0.0.1/0\n'),
|
when(mockFileSystem.file('test')).thenReturn(mockFile);
|
||||||
]),
|
when(mockFile.existsSync()).thenReturn(true);
|
||||||
stderr: const Stream<List<int>>.empty(),
|
when(macOSApp.executable).thenReturn('test');
|
||||||
);
|
when(mockProcessManager.start(<String>['test'])).thenAnswer((Invocation invocation) async {
|
||||||
});
|
return FakeProcess(
|
||||||
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
exitCode: Completer<int>().future,
|
||||||
return ProcessResult(0, 1, '', '');
|
stdout: Stream<List<int>>.fromIterable(<List<int>>[
|
||||||
|
utf8.encode('Observatory listening on http://127.0.0.1/0\n'),
|
||||||
|
]),
|
||||||
|
stderr: const Stream<List<int>>.empty(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
||||||
|
return ProcessResult(0, 1, '', '');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('Can run from prebuilt application', () async {
|
testUsingContext('can run from prebuilt application', () async {
|
||||||
final LaunchResult result = await device.startApp(macOSApp, prebuiltApplication: true);
|
final LaunchResult result = await device.startApp(macOSApp, prebuiltApplication: true);
|
||||||
expect(result.started, true);
|
expect(result.started, true);
|
||||||
expect(result.observatoryUri, Uri.parse('http://127.0.0.1/0'));
|
expect(result.observatoryUri, Uri.parse('http://127.0.0.1/0'));
|
||||||
|
@ -137,7 +144,7 @@ tester 17193 0.0 0.2 4791128 37820 ?? S 2:27PM 0:00.09 /Applica
|
||||||
|
|
||||||
class MockPlatform extends Mock implements Platform {}
|
class MockPlatform extends Mock implements Platform {}
|
||||||
|
|
||||||
class MockMacOSApp extends Mock implements MacOSApp {}
|
class MockMacOSApp extends Mock implements PrebuiltMacOSApp {}
|
||||||
|
|
||||||
class MockFileSystem extends Mock implements FileSystem {}
|
class MockFileSystem extends Mock implements FileSystem {}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ void main() {
|
||||||
fullRestart: anyNamed('fullRestart'),
|
fullRestart: anyNamed('fullRestart'),
|
||||||
projectRootPath: anyNamed('projectRootPath'),
|
projectRootPath: anyNamed('projectRootPath'),
|
||||||
pathToReload: anyNamed('pathToReload'),
|
pathToReload: anyNamed('pathToReload'),
|
||||||
|
dillOutputPath: anyNamed('dillOutputPath'),
|
||||||
)).thenAnswer((Invocation invocation) async {
|
)).thenAnswer((Invocation invocation) async {
|
||||||
return UpdateFSReport(
|
return UpdateFSReport(
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -175,6 +176,7 @@ void main() {
|
||||||
projectRootPath: anyNamed('projectRootPath'),
|
projectRootPath: anyNamed('projectRootPath'),
|
||||||
pathToReload: anyNamed('pathToReload'),
|
pathToReload: anyNamed('pathToReload'),
|
||||||
invalidatedFiles: anyNamed('invalidatedFiles'),
|
invalidatedFiles: anyNamed('invalidatedFiles'),
|
||||||
|
dillOutputPath: anyNamed('dillOutputPath'),
|
||||||
)).thenThrow(RpcException(666, 'something bad happened'));
|
)).thenThrow(RpcException(666, 'something bad happened'));
|
||||||
|
|
||||||
final OperationResult result = await residentRunner.restart(fullRestart: false);
|
final OperationResult result = await residentRunner.restart(fullRestart: false);
|
||||||
|
@ -279,6 +281,7 @@ void main() {
|
||||||
projectRootPath: anyNamed('projectRootPath'),
|
projectRootPath: anyNamed('projectRootPath'),
|
||||||
pathToReload: anyNamed('pathToReload'),
|
pathToReload: anyNamed('pathToReload'),
|
||||||
invalidatedFiles: anyNamed('invalidatedFiles'),
|
invalidatedFiles: anyNamed('invalidatedFiles'),
|
||||||
|
dillOutputPath: anyNamed('dillOutputPath'),
|
||||||
)).thenThrow(RpcException(666, 'something bad happened'));
|
)).thenThrow(RpcException(666, 'something bad happened'));
|
||||||
|
|
||||||
final OperationResult result = await residentRunner.restart(fullRestart: true);
|
final OperationResult result = await residentRunner.restart(fullRestart: true);
|
||||||
|
|
Loading…
Reference in a new issue