Add Xcode build script for macOS target (#31329)
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index cc446c2..b2080eb 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -18,6 +18,7 @@
flutterTester,
snapshotDart,
flutterFramework,
+ flutterMacOSFramework,
vmSnapshotData,
isolateSnapshotData,
platformKernelDill,
@@ -44,6 +45,8 @@
return 'snapshot.dart';
case Artifact.flutterFramework:
return 'Flutter.framework';
+ case Artifact.flutterMacOSFramework:
+ return 'FlutterMacOS.framework';
case Artifact.vmSnapshotData:
// Flutter 'debug' and 'dynamic profile' modes for all target platforms use Dart
// RELEASE VM snapshot that comes from host debug build and has the metadata
@@ -211,6 +214,10 @@
return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact));
case Artifact.kernelWorkerSnapshot:
return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact));
+ case Artifact.flutterMacOSFramework:
+ final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
+ final String platformDirName = getNameForTargetPlatform(platform);
+ return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
default:
assert(false, 'Artifact $artifact not available for platform $platform.');
return null;
@@ -279,6 +286,8 @@
return fs.path.join(_getFlutterPatchedSdkPath(mode), 'lib', _artifactToFileName(artifact));
case Artifact.flutterFramework:
return fs.path.join(engineOutPath, _artifactToFileName(artifact));
+ case Artifact.flutterMacOSFramework:
+ return fs.path.join(engineOutPath, _artifactToFileName(artifact));
case Artifact.flutterPatchedSdkPath:
// When using local engine always use [BuildMode.debug] regardless of
// what was specified in [mode] argument because local engine will
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index a95da5b..d0ad3d6 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -382,6 +382,11 @@
return fs.path.join(getBuildDirectory(), 'ios');
}
+/// Returns the macOS build output directory.
+String getMacOSBuildDirectory() {
+ return fs.path.join(getBuildDirectory(), 'macos');
+}
+
/// Returns the web build output directory.
String getWebBuildDirectory() {
return fs.path.join(getBuildDirectory(), 'web');
diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart
index 091f9fd..3d909ed 100644
--- a/packages/flutter_tools/lib/src/commands/build_bundle.dart
+++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart
@@ -8,6 +8,7 @@
import '../build_info.dart';
import '../bundle.dart';
import '../runner/flutter_command.dart' show FlutterOptions, FlutterCommandResult;
+import '../version.dart';
import 'build.dart';
class BuildBundleCommand extends BuildSubCommand {
@@ -27,7 +28,16 @@
..addOption('depfile', defaultsTo: defaultDepfilePath)
..addOption('target-platform',
defaultsTo: 'android-arm',
- allowed: <String>['android-arm', 'android-arm64', 'android-x86', 'android-x64', 'ios'],
+ allowed: const <String>[
+ 'android-arm',
+ 'android-arm64',
+ 'android-x86',
+ 'android-x64',
+ 'ios',
+ 'darwin-x64',
+ 'linux-x64',
+ 'windows-x64',
+ ],
)
..addFlag('track-widget-creation',
hide: !verboseHelp,
@@ -64,8 +74,21 @@
Future<FlutterCommandResult> runCommand() async {
final String targetPlatform = argResults['target-platform'];
final TargetPlatform platform = getTargetPlatformForName(targetPlatform);
- if (platform == null)
+ if (platform == null) {
throwToolExit('Unknown platform: $targetPlatform');
+ }
+ // Check for target platforms that are only allowed on unstable Flutter.
+ switch (platform) {
+ case TargetPlatform.darwin_x64:
+ case TargetPlatform.windows_x64:
+ case TargetPlatform.linux_x64:
+ if (FlutterVersion.instance.isStable) {
+ throwToolExit('$targetPlatform is not supported on stable Flutter.');
+ }
+ break;
+ default:
+ break;
+ }
final BuildMode buildMode = getBuildMode();
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
index c929186..e6e3ac8 100644
--- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -27,14 +27,23 @@
Artifact.flutterFramework, platform: TargetPlatform.ios, mode: mode)));
}
+String flutterMacOSFrameworkDir(BuildMode mode) {
+ return fs.path.normalize(fs.path.dirname(artifacts.getArtifactPath(
+ Artifact.flutterMacOSFramework, platform: TargetPlatform.darwin_x64, mode: mode)));
+}
+
/// Writes or rewrites Xcode property files with the specified information.
///
+/// useMacOSConfig: Optional parameter that controls whether we use the macOS
+/// project file instead. Defaults to false.
+///
/// targetOverride: Optional parameter, if null or unspecified the default value
/// from xcode_backend.sh is used 'lib/main.dart'.
Future<void> updateGeneratedXcodeProperties({
@required FlutterProject project,
@required BuildInfo buildInfo,
String targetOverride,
+ bool useMacOSConfig = false,
}) async {
final StringBuffer localsBuffer = StringBuffer();
@@ -53,14 +62,20 @@
// The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
localsBuffer.writeln('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
- localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
+ final String buildDirectory = useMacOSConfig
+ ? getMacOSBuildDirectory()
+ : getIosBuildDirectory();
+ localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../$buildDirectory');
if (!project.isModule) {
// For module projects we do not want to write the FLUTTER_FRAMEWORK_DIR
// explicitly. Rather we rely on the xcode backend script and the Podfile
// logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE.
// However, this is necessary for regular projects using Cocoapods.
- localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');
+ final String frameworkDir = useMacOSConfig
+ ? flutterMacOSFrameworkDir(buildInfo.mode)
+ : flutterFrameworkDir(buildInfo.mode);
+ localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=$frameworkDir');
}
final String buildName = validatedBuildNameForPlatform(TargetPlatform.ios, buildInfo?.buildName ?? project.manifest.buildName);
@@ -85,15 +100,21 @@
// NOTE: this assumes that local engine binary paths are consistent with
// the conventions uses in the engine: 32-bit iOS engines are built to
// paths ending in _arm, 64-bit builds are not.
- final String arch = engineOutPath.endsWith('_arm') ? 'armv7' : 'arm64';
- localsBuffer.writeln('ARCHS=$arch');
+ //
+ // Skip this step for macOS builds.
+ if (!useMacOSConfig) {
+ final String arch = engineOutPath.endsWith('_arm') ? 'armv7' : 'arm64';
+ localsBuffer.writeln('ARCHS=$arch');
+ }
}
if (buildInfo.trackWidgetCreation) {
localsBuffer.writeln('TRACK_WIDGET_CREATION=true');
}
- final File generatedXcodePropertiesFile = project.ios.generatedXcodePropertiesFile;
+ final File generatedXcodePropertiesFile = useMacOSConfig
+ ? project.macos.generatedXcodePropertiesFile
+ : project.ios.generatedXcodePropertiesFile;
generatedXcodePropertiesFile.createSync(recursive: true);
generatedXcodePropertiesFile.writeAsStringSync(localsBuffer.toString());
}
diff --git a/packages/flutter_tools/lib/src/macos/build_macos.dart b/packages/flutter_tools/lib/src/macos/build_macos.dart
index 4c149be..bba4df0 100644
--- a/packages/flutter_tools/lib/src/macos/build_macos.dart
+++ b/packages/flutter_tools/lib/src/macos/build_macos.dart
@@ -3,22 +3,46 @@
// found in the LICENSE file.
import '../base/common.dart';
+import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process_manager.dart';
import '../build_info.dart';
-import '../cache.dart';
import '../convert.dart';
import '../globals.dart';
+import '../ios/xcodeproj.dart';
import '../project.dart';
-/// Builds the macOS project through the project shell script.
+/// Builds the macOS project through xcode build.
+// TODO(jonahwilliams): support target option.
+// TODO(jonahwilliams): refactor to share code with the existing iOS code.
Future<void> buildMacOS(FlutterProject flutterProject, BuildInfo buildInfo) async {
+ // Write configuration to an xconfig file in a standard location.
+ await updateGeneratedXcodeProperties(
+ project: flutterProject,
+ buildInfo: buildInfo,
+ useMacOSConfig: true,
+ );
+ // Set debug or release mode.
+ String config = 'Debug';
+ if (buildInfo.isRelease) {
+ config = 'Release';
+ }
+ final Directory flutterBuildDir = fs.directory(getMacOSBuildDirectory());
+ if (!flutterBuildDir.existsSync()) {
+ flutterBuildDir.createSync(recursive: true);
+ }
+ // Run build script provided by application.
final Process process = await processManager.start(<String>[
- flutterProject.macos.buildScript.path,
- Cache.flutterRoot,
- buildInfo?.isDebug == true ? 'debug' : 'release',
- buildInfo?.trackWidgetCreation == true ? 'track-widget-creation' : 'no-track-widget-creation',
+ '/usr/bin/env',
+ 'xcrun',
+ 'xcodebuild',
+ '-project', flutterProject.macos.xcodeProjectFile.path,
+ '-configuration', '$config',
+ '-scheme', 'Runner',
+ '-derivedDataPath', flutterBuildDir.absolute.path,
+ 'OBJROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
+ 'SYMROOT=${fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
], runInShell: true);
final Status status = logger.startProgress(
'Building macOS application...',
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index 2cd22e8..d387527 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -536,8 +536,14 @@
bool existsSync() => project.directory.childDirectory('macos').existsSync();
- // Note: The build script file exists as a temporary shim.
- File get buildScript => project.directory.childDirectory('macos').childFile('build.sh');
+ Directory get _editableDirectory => project.directory.childDirectory('macos');
+
+ /// Contains definitions for FLUTTER_ROOT, LOCAL_ENGINE, and more flags for
+ /// the Xcode build.
+ File get generatedXcodePropertiesFile => _editableDirectory.childDirectory('Flutter').childFile('Generated.xcconfig');
+
+ /// The Xcode project file.
+ Directory get xcodeProjectFile => _editableDirectory.childDirectory('Runner.xcodeproj');
// Note: The name script file exists as a temporary shim.
File get nameScript => project.directory.childDirectory('macos').childFile('name_output.sh');