Unverified Commit fef69b20 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Partial macOS assemble revert (#37664)

parent 0bba4728
......@@ -3,6 +3,8 @@
# Use of this source code is governed by a BSD-style license that can be
# 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() {
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
echo "♦ $*"
......@@ -31,6 +33,18 @@ if [[ -n "$FLUTTER_TARGET" ]]; then
target_path="${FLUTTER_TARGET}"
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
flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}"
fi
......@@ -49,48 +63,22 @@ if [[ -n "$LOCAL_ENGINE" ]]; then
exit -1
fi
local_engine_flag="--local-engine=${LOCAL_ENGINE}"
flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/${framework_name}"
fi
RunCommand mkdir -p -- "$ephemeral_dir"
RunCommand rm -rf -- "${ephemeral_dir}/${framework_name}"
RunCommand cp -Rp -- "${flutter_framework}" "${ephemeral_dir}"
# Set the build mode
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"
# Precache artifacts
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
${verbose_flag} \
precache \
--no-android \
--no-ios \
--macos
# TODO(jonahwilliams): support flavors https://github.com/flutter/flutter/issues/32923
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
${verbose_flag} \
build bundle \
--target-platform=darwin-x64 \
--target="${target_path}" \
--${build_mode} \
${track_widget_creation_flag} \
${flutter_engine_flag} \
${local_engine_flag} \
assemble \
-dTargetFile="${target_path}" \
-dTargetPlatform=darwin-x64 \
-dBuildMode=debug \
--build-inputs="${build_inputs_path}" \
--build-outputs="${build_outputs_path}" \
"${build_target}"
${local_engine_flag}
......@@ -6,6 +6,7 @@ import 'package:meta/meta.dart';
import '../application_package.dart';
import '../base/file_system.dart';
import '../build_info.dart';
import '../globals.dart';
import '../ios/plist_utils.dart' as plist;
import '../project.dart';
......@@ -30,17 +31,18 @@ abstract class MacOSApp extends ApplicationPackage {
/// which is expected to start the application and send the observatory
/// port over stdout.
factory MacOSApp.fromPrebuiltApp(FileSystemEntity applicationBinary) {
final ExecutableAndId executableAndId = executableFromBundle(applicationBinary);
final _ExecutableAndId executableAndId = _executableFromBundle(applicationBinary);
final Directory applicationBundle = fs.directory(applicationBinary);
return PrebuiltMacOSApp(
bundleDir: applicationBundle,
bundleName: applicationBundle.path,
executableAndId: executableAndId,
projectBundleId: executableAndId.id,
executable: executableAndId.executable,
);
}
/// 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);
if (entityType == FileSystemEntityType.notFound) {
printError('File "${applicationBundle.path}" does not exist.');
......@@ -73,28 +75,40 @@ abstract class MacOSApp extends ApplicationPackage {
if (!fs.file(executable).existsSync()) {
printError('Could not find macOS binary at $executable');
}
return ExecutableAndId(executable, id);
return _ExecutableAndId(executable, id);
}
@override
String get displayName => id;
String applicationBundle(BuildMode buildMode);
String executable(BuildMode buildMode);
}
class PrebuiltMacOSApp extends MacOSApp {
PrebuiltMacOSApp({
@required this.bundleDir,
@required this.bundleName,
@required this.executableAndId,
});
@required this.projectBundleId,
@required String executable,
}) : _executable = executable,
super(projectBundleId: projectBundleId);
final Directory bundleDir;
final String bundleName;
final ExecutableAndId executableAndId;
final String projectBundleId;
final String _executable;
@override
String get name => bundleName;
String get executable => executableAndId.executable;
@override
String applicationBundle(BuildMode buildMode) => bundleDir.path;
@override
String executable(BuildMode buildMode) => _executable;
}
class BuildableMacOSApp extends MacOSApp {
......@@ -104,10 +118,35 @@ class BuildableMacOSApp extends MacOSApp {
@override
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 {
ExecutableAndId(this.executable, this.id);
class _ExecutableAndId {
_ExecutableAndId(this.executable, this.id);
final String executable;
final String id;
......
......@@ -8,35 +8,25 @@ import '../base/io.dart';
import '../base/logger.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
import '../build_system/build_system.dart';
import '../build_system/targets/dart.dart';
import '../convert.dart';
import '../globals.dart';
import '../ios/xcodeproj.dart';
import '../project.dart';
import '../reporting/reporting.dart';
import 'application_package.dart';
/// Builds the macOS project through xcodebuild and returns the app bundle.
Future<PrebuiltMacOSApp> buildMacOS({
import 'cocoapod_utils.dart';
/// Builds the macOS project through xcodebuild.
// TODO(jonahwilliams): refactor to share code with the existing iOS code.
Future<void> buildMacOS({
FlutterProject flutterProject,
BuildInfo buildInfo,
String targetOverride = 'lib/main.dart',
String targetOverride,
}) async {
// Create the environment used to process the build. This needs to match what
// is provided in bin/macos_build_flutter_assets.sh otherwise the directories
// will be different.
final Environment environment = Environment(
projectDir: flutterProject.directory,
buildDir: flutterProject.dartTool.childDirectory('flutter_build'),
defines: <String, String>{
// TODO(jonahwilliams): support other build types.
kBuildMode: 'debug',
kTargetPlatform: 'darwin-x64',
kTargetFile: targetOverride,
},
);
final Directory flutterBuildDir = fs.directory(getMacOSBuildDirectory());
if (!flutterBuildDir.existsSync()) {
flutterBuildDir.createSync(recursive: true);
}
// Write configuration to an xconfig file in a standard location.
await updateGeneratedXcodeProperties(
project: flutterProject,
......@@ -44,34 +34,27 @@ Future<PrebuiltMacOSApp> buildMacOS({
targetOverride: targetOverride,
useMacOSConfig: true,
setSymroot: false,
buildDirOverride: environment.buildDir.path,
);
// 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);
}
await processPodsIfNeeded(flutterProject.macos, getMacOSBuildDirectory(), buildInfo.mode);
// Set debug or release mode.
String config = 'Debug';
if (buildInfo.isRelease ?? false) {
if (buildInfo.isRelease) {
config = 'Release';
}
// Invoke Xcode with correct configuration.
// Run build script provided by application.
final Stopwatch sw = Stopwatch()..start();
final List<String> command = <String>[
final Process process = await processManager.start(<String>[
'/usr/bin/env',
'xcrun',
'xcodebuild',
'-workspace', flutterProject.macos.xcodeWorkspace.path,
'-configuration', config,
'-configuration', '$config',
'-scheme', 'Runner',
'-derivedDataPath', environment.buildDir.path,
'OBJROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Intermediates.noindex')}',
'SYMROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Products')}',
];
final Process process = await processManager.start(command);
'-derivedDataPath', flutterBuildDir.absolute.path,
'OBJROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
'SYMROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
]);
final Status status = logger.startProgress(
'Building macOS application...',
timeout: null,
......@@ -94,13 +77,4 @@ Future<PrebuiltMacOSApp> buildMacOS({
throwToolExit('Build process failed');
}
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);
}
}
\ No newline at end of file
......@@ -69,8 +69,6 @@ class MacOSDevice extends Device {
@override
Future<String> get sdkNameAndVersion async => os.name;
String _cachedExecutable;
@override
Future<LaunchResult> startApp(
covariant MacOSApp package, {
......@@ -82,30 +80,27 @@ class MacOSDevice extends Device {
bool usesTerminalUi = true,
bool ipv6 = false,
}) async {
Cache.releaseLockEarly();
// Stop any running applications with the same executable.
PrebuiltMacOSApp prebuiltMacOSApp;
if (prebuiltApplication) {
prebuiltMacOSApp = package;
} else {
prebuiltMacOSApp = await buildMacOS(
if (!prebuiltApplication) {
Cache.releaseLockEarly();
await buildMacOS(
flutterProject: FlutterProject.current(),
buildInfo: debuggingOptions?.buildInfo,
targetOverride: mainPath,
);
}
_cachedExecutable = prebuiltMacOSApp.executable;
// Ensure that the executable is locatable.
if (prebuiltMacOSApp == null) {
final String executable = package.executable(debuggingOptions?.buildInfo?.mode);
if (executable == null) {
printError('Unable to find executable to run');
return LaunchResult.failed();
}
// Make sure to call stop app after we've built.
await stopApp(prebuiltMacOSApp);
await stopApp(package);
final Process process = await processManager.start(<String>[
prebuiltMacOSApp.executable,
executable
]);
if (debuggingOptions?.buildInfo?.isRelease == true) {
return LaunchResult.succeeded();
......@@ -116,7 +111,7 @@ class MacOSDevice extends Device {
final Uri observatoryUri = await observatoryDiscovery.uri;
// Bring app to foreground.
await processManager.run(<String>[
'open', prebuiltMacOSApp.bundleName,
'open', package.applicationBundle(debuggingOptions?.buildInfo?.mode),
]);
return LaunchResult.succeeded(observatoryUri: observatoryUri);
} catch (error) {
......@@ -131,7 +126,7 @@ class MacOSDevice extends Device {
// currently we rely on killing the isolate taking down the application.
@override
Future<bool> stopApp(covariant MacOSApp app) async {
return killProcess(_cachedExecutable);
return killProcess(app.executable(BuildMode.debug));
}
@override
......
......@@ -8,8 +8,7 @@ import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/features.dart';
......@@ -87,15 +86,7 @@ void main() {
fs.file('.packages').createSync();
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
final FlutterProject flutterProject = FlutterProject.fromDirectory(fs.currentDirectory);
final Environment environment = Environment(
projectDir: flutterProject.directory,
buildDir: flutterProject.dartTool.childDirectory('flutter_build'),
defines: <String, String>{
kBuildMode: 'debug',
kTargetFile: 'lib/main.dart',
kTargetPlatform: 'darwin-x64',
}
);
final Directory flutterBuildDir = fs.directory(getMacOSBuildDirectory());
when(mockProcessManager.start(<String>[
'/usr/bin/env',
'xcrun',
......@@ -103,9 +94,9 @@ void main() {
'-workspace', flutterProject.macos.xcodeWorkspace.path,
'-configuration', 'Release',
'-scheme', 'Runner',
'-derivedDataPath', environment.buildDir.path,
'OBJROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Intermediates.noindex')}',
'SYMROOT=${fs.path.join(environment.buildDir.path, 'Build', 'Products')}',
'-derivedDataPath', flutterBuildDir.absolute.path,
'OBJROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
'SYMROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
])).thenAnswer((Invocation invocation) async {
fs.file(fs.path.join('macos', 'Flutter', 'ephemeral', '.app_filename'))
..createSync(recursive: true)
......@@ -113,9 +104,9 @@ void main() {
return mockProcess;
});
expect(createTestCommandRunner(command).run(
await createTestCommandRunner(command).run(
const <String>['build', 'macos', '--release']
), throwsA(isInstanceOf<AssertionError>()));
);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
......
......@@ -36,6 +36,7 @@ void main() {
testUsingContext('defaults', () async {
final MockMacOSApp mockMacOSApp = MockMacOSApp();
when(mockMacOSApp.executable(any)).thenReturn('foo');
expect(await device.targetPlatform, TargetPlatform.darwin_x64);
expect(device.name, 'macOS');
expect(await device.installApp(mockMacOSApp), true);
......@@ -48,36 +49,46 @@ void main() {
ProcessManager: () => mockProcessManager,
});
testUsingContext('stopApp', () async {
const String psOut = r'''
tester 17193 0.0 0.2 4791128 37820 ?? S 2:27PM 0:00.09 /Applications/foo
''';
final MockMacOSApp mockMacOSApp = MockMacOSApp();
when(mockMacOSApp.executable(any)).thenReturn('/Applications/foo');
when(mockProcessManager.run(<String>['ps', 'aux'])).thenAnswer((Invocation invocation) async {
return ProcessResult(1, 0, psOut, '');
});
when(mockProcessManager.run(<String>['kill', '17193'])).thenAnswer((Invocation invocation) async {
return ProcessResult(2, 0, '', '');
});
expect(await device.stopApp(mockMacOSApp), true);
verify(mockProcessManager.run(<String>['kill', '17193']));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
group('startApp', () {
MockMacOSApp macOSApp;
MockFileSystem mockFileSystem;
MockProcessManager mockProcessManager;
MockFile mockFile;
setUp(() {
macOSApp = MockMacOSApp();
mockFileSystem = MockFileSystem();
mockProcessManager = MockProcessManager();
mockFile = MockFile();
when(mockFileSystem.file('test')).thenReturn(mockFile);
when(mockFile.existsSync()).thenReturn(true);
when(macOSApp.executable).thenReturn('test');
when(mockProcessManager.start(<String>['test'])).thenAnswer((Invocation invocation) async {
return FakeProcess(
exitCode: Completer<int>().future,
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, '', '');
});
final MockMacOSApp macOSApp = MockMacOSApp();
final MockFileSystem mockFileSystem = MockFileSystem();
final MockProcessManager mockProcessManager = MockProcessManager();
final MockFile mockFile = MockFile();
when(macOSApp.executable(any)).thenReturn('test');
when(mockFileSystem.file('test')).thenReturn(mockFile);
when(mockFile.existsSync()).thenReturn(true);
when(mockProcessManager.start(<String>['test'])).thenAnswer((Invocation invocation) async {
return FakeProcess(
exitCode: Completer<int>().future,
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);
expect(result.started, true);
expect(result.observatoryUri, Uri.parse('http://127.0.0.1/0'));
......@@ -126,7 +137,7 @@ void main() {
class MockPlatform extends Mock implements Platform {}
class MockMacOSApp extends Mock implements PrebuiltMacOSApp {}
class MockMacOSApp extends Mock implements MacOSApp {}
class MockFileSystem extends Mock implements FileSystem {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment