201 lines
7.3 KiB
Dart
201 lines
7.3 KiB
Dart
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:meta/meta.dart';
|
|
|
|
import '../artifacts.dart';
|
|
import '../base/file_system.dart';
|
|
import '../base/process.dart';
|
|
import '../base/utils.dart';
|
|
import '../build_info.dart';
|
|
import '../cache.dart';
|
|
import '../globals.dart';
|
|
|
|
final RegExp _settingExpr = new RegExp(r'(\w+)\s*=\s*(\S+)');
|
|
final RegExp _varExpr = new RegExp(r'\$\((.*)\)');
|
|
|
|
String flutterFrameworkDir(BuildMode mode) {
|
|
return fs.path.normalize(fs.path.dirname(artifacts.getArtifactPath(Artifact.flutterFramework, TargetPlatform.ios, mode)));
|
|
}
|
|
|
|
void updateXcodeGeneratedProperties({
|
|
@required String projectPath,
|
|
@required BuildInfo buildInfo,
|
|
@required String target,
|
|
@required bool hasPlugins,
|
|
@required bool previewDart2,
|
|
}) {
|
|
final StringBuffer localsBuffer = new StringBuffer();
|
|
|
|
localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
|
|
|
|
final String flutterRoot = fs.path.normalize(Cache.flutterRoot);
|
|
localsBuffer.writeln('FLUTTER_ROOT=$flutterRoot');
|
|
|
|
// This holds because requiresProjectRoot is true for this command
|
|
localsBuffer.writeln('FLUTTER_APPLICATION_PATH=${fs.path.normalize(projectPath)}');
|
|
|
|
// Relative to FLUTTER_APPLICATION_PATH, which is [Directory.current].
|
|
localsBuffer.writeln('FLUTTER_TARGET=$target');
|
|
|
|
// The runtime mode for the current build.
|
|
localsBuffer.writeln('FLUTTER_BUILD_MODE=${buildInfo.modeName}');
|
|
|
|
// The build outputs directory, relative to FLUTTER_APPLICATION_PATH.
|
|
localsBuffer.writeln('FLUTTER_BUILD_DIR=${getBuildDirectory()}');
|
|
|
|
localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');
|
|
|
|
localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');
|
|
|
|
if (artifacts is LocalEngineArtifacts) {
|
|
final LocalEngineArtifacts localEngineArtifacts = artifacts;
|
|
localsBuffer.writeln('LOCAL_ENGINE=${localEngineArtifacts.engineOutPath}');
|
|
}
|
|
|
|
if (previewDart2) {
|
|
localsBuffer.writeln('PREVIEW_DART_2=true');
|
|
localsBuffer.writeln('STRONG=true');
|
|
}
|
|
|
|
// Add dependency to CocoaPods' generated project only if plugins are used.
|
|
if (hasPlugins)
|
|
localsBuffer.writeln('#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"');
|
|
|
|
final File localsFile = fs.file(fs.path.join(projectPath, 'ios', 'Flutter', 'Generated.xcconfig'));
|
|
localsFile.createSync(recursive: true);
|
|
localsFile.writeAsStringSync(localsBuffer.toString());
|
|
}
|
|
|
|
Map<String, String> getXcodeBuildSettings(String xcodeProjPath, String target) {
|
|
final String absProjPath = fs.path.absolute(xcodeProjPath);
|
|
final String out = runCheckedSync(<String>[
|
|
'/usr/bin/xcodebuild', '-project', absProjPath, '-target', target, '-showBuildSettings'
|
|
]);
|
|
final Map<String, String> settings = <String, String>{};
|
|
for (String line in out.split('\n').where(_settingExpr.hasMatch)) {
|
|
final Match match = _settingExpr.firstMatch(line);
|
|
settings[match[1]] = match[2];
|
|
}
|
|
return settings;
|
|
}
|
|
|
|
/// Substitutes variables in [str] with their values from the specified Xcode
|
|
/// project and target.
|
|
String substituteXcodeVariables(String str, Map<String, String> xcodeBuildSettings) {
|
|
final Iterable<Match> matches = _varExpr.allMatches(str);
|
|
if (matches.isEmpty)
|
|
return str;
|
|
|
|
return str.replaceAllMapped(_varExpr, (Match m) => xcodeBuildSettings[m[1]] ?? m[0]);
|
|
}
|
|
|
|
/// Information about an Xcode project.
|
|
///
|
|
/// Represents the output of `xcodebuild -list`.
|
|
class XcodeProjectInfo {
|
|
XcodeProjectInfo(this.targets, this.buildConfigurations, this.schemes);
|
|
|
|
factory XcodeProjectInfo.fromProjectSync(String projectPath) {
|
|
final String out = runCheckedSync(<String>[
|
|
'/usr/bin/xcodebuild', '-list',
|
|
], workingDirectory: projectPath);
|
|
return new XcodeProjectInfo.fromXcodeBuildOutput(out);
|
|
}
|
|
|
|
factory XcodeProjectInfo.fromXcodeBuildOutput(String output) {
|
|
final List<String> targets = <String>[];
|
|
final List<String> buildConfigurations = <String>[];
|
|
final List<String> schemes = <String>[];
|
|
List<String> collector;
|
|
for (String line in output.split('\n')) {
|
|
if (line.isEmpty) {
|
|
collector = null;
|
|
continue;
|
|
} else if (line.endsWith('Targets:')) {
|
|
collector = targets;
|
|
continue;
|
|
} else if (line.endsWith('Build Configurations:')) {
|
|
collector = buildConfigurations;
|
|
continue;
|
|
} else if (line.endsWith('Schemes:')) {
|
|
collector = schemes;
|
|
continue;
|
|
}
|
|
collector?.add(line.trim());
|
|
}
|
|
if (schemes.isEmpty)
|
|
schemes.add('Runner');
|
|
return new XcodeProjectInfo(targets, buildConfigurations, schemes);
|
|
}
|
|
|
|
final List<String> targets;
|
|
final List<String> buildConfigurations;
|
|
final List<String> schemes;
|
|
|
|
bool get definesCustomTargets => !(targets.contains('Runner') && targets.length == 1);
|
|
bool get definesCustomSchemes => !(schemes.contains('Runner') && schemes.length == 1);
|
|
bool get definesCustomBuildConfigurations {
|
|
return !(buildConfigurations.contains('Debug') &&
|
|
buildConfigurations.contains('Release') &&
|
|
buildConfigurations.length == 2);
|
|
}
|
|
|
|
/// The expected scheme for [buildInfo].
|
|
static String expectedSchemeFor(BuildInfo buildInfo) {
|
|
return toTitleCase(buildInfo.flavor ?? 'runner');
|
|
}
|
|
|
|
/// The expected build configuration for [buildInfo] and [scheme].
|
|
static String expectedBuildConfigurationFor(BuildInfo buildInfo, String scheme) {
|
|
final String baseConfiguration = _baseConfigurationFor(buildInfo);
|
|
if (buildInfo.flavor == null)
|
|
return baseConfiguration;
|
|
else
|
|
return baseConfiguration + '-$scheme';
|
|
}
|
|
|
|
/// Returns unique scheme matching [buildInfo], or null, if there is no unique
|
|
/// best match.
|
|
String schemeFor(BuildInfo buildInfo) {
|
|
final String expectedScheme = expectedSchemeFor(buildInfo);
|
|
if (schemes.contains(expectedScheme))
|
|
return expectedScheme;
|
|
return _uniqueMatch(schemes, (String candidate) {
|
|
return candidate.toLowerCase() == expectedScheme.toLowerCase();
|
|
});
|
|
}
|
|
|
|
/// Returns unique build configuration matching [buildInfo] and [scheme], or
|
|
/// null, if there is no unique best match.
|
|
String buildConfigurationFor(BuildInfo buildInfo, String scheme) {
|
|
final String expectedConfiguration = expectedBuildConfigurationFor(buildInfo, scheme);
|
|
if (buildConfigurations.contains(expectedConfiguration))
|
|
return expectedConfiguration;
|
|
final String baseConfiguration = _baseConfigurationFor(buildInfo);
|
|
return _uniqueMatch(buildConfigurations, (String candidate) {
|
|
candidate = candidate.toLowerCase();
|
|
if (buildInfo.flavor == null)
|
|
return candidate == expectedConfiguration.toLowerCase();
|
|
else
|
|
return candidate.contains(baseConfiguration.toLowerCase()) && candidate.contains(scheme.toLowerCase());
|
|
});
|
|
}
|
|
|
|
static String _baseConfigurationFor(BuildInfo buildInfo) => buildInfo.isDebug ? 'Debug' : 'Release';
|
|
|
|
static String _uniqueMatch(Iterable<String> strings, bool matches(String s)) {
|
|
final List<String> options = strings.where(matches).toList();
|
|
if (options.length == 1)
|
|
return options.first;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return 'XcodeProjectInfo($targets, $buildConfigurations, $schemes)';
|
|
}
|
|
}
|