mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
1052 lines
35 KiB
Dart
1052 lines
35 KiB
Dart
// Copyright 2014 The Flutter 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:package_config/package_config_types.dart';
|
|
|
|
import 'base/config.dart';
|
|
import 'base/context.dart';
|
|
import 'base/file_system.dart';
|
|
import 'base/logger.dart';
|
|
import 'base/os.dart';
|
|
import 'base/utils.dart';
|
|
import 'convert.dart';
|
|
import 'globals.dart' as globals;
|
|
|
|
/// Whether icon font subsetting is enabled by default.
|
|
const bool kIconTreeShakerEnabledDefault = true;
|
|
|
|
/// Information about a build to be performed or used.
|
|
class BuildInfo {
|
|
const BuildInfo(
|
|
this.mode,
|
|
this.flavor, {
|
|
this.trackWidgetCreation = false,
|
|
List<String>? extraFrontEndOptions,
|
|
List<String>? extraGenSnapshotOptions,
|
|
List<String>? fileSystemRoots,
|
|
this.androidProjectArgs = const <String>[],
|
|
this.fileSystemScheme,
|
|
this.buildNumber,
|
|
this.buildName,
|
|
this.splitDebugInfoPath,
|
|
this.dartObfuscation = false,
|
|
List<String>? dartDefines,
|
|
this.bundleSkSLPath,
|
|
List<String>? dartExperiments,
|
|
required this.treeShakeIcons,
|
|
this.performanceMeasurementFile,
|
|
this.packagesPath = '.dart_tool/package_config.json', // TODO(zanderso): make this required and remove the default.
|
|
this.nullSafetyMode = NullSafetyMode.sound,
|
|
this.codeSizeDirectory,
|
|
this.androidGradleDaemon = true,
|
|
this.packageConfig = PackageConfig.empty,
|
|
this.initializeFromDill,
|
|
this.assumeInitializeFromDillUpToDate = false,
|
|
}) : extraFrontEndOptions = extraFrontEndOptions ?? const <String>[],
|
|
extraGenSnapshotOptions = extraGenSnapshotOptions ?? const <String>[],
|
|
fileSystemRoots = fileSystemRoots ?? const <String>[],
|
|
dartDefines = dartDefines ?? const <String>[],
|
|
dartExperiments = dartExperiments ?? const <String>[];
|
|
|
|
final BuildMode mode;
|
|
|
|
/// The null safety mode the application should be run in.
|
|
///
|
|
/// If not provided, defaults to [NullSafetyMode.autodetect].
|
|
final NullSafetyMode nullSafetyMode;
|
|
|
|
/// Whether the build should subset icon fonts.
|
|
final bool treeShakeIcons;
|
|
|
|
/// Represents a custom Android product flavor or an Xcode scheme, null for
|
|
/// using the default.
|
|
///
|
|
/// If not null, the Gradle build task will be `assembleFlavorMode` (e.g.
|
|
/// `assemblePaidRelease`), and the Xcode build configuration will be
|
|
/// Mode-Flavor (e.g. Release-Paid).
|
|
final String? flavor;
|
|
|
|
/// The path to the package configuration file to use for compilation.
|
|
///
|
|
/// This is used by package:package_config to locate the actual package_config.json
|
|
/// file. If not provided, defaults to `.dart_tool/packages`.
|
|
final String packagesPath;
|
|
|
|
final List<String> fileSystemRoots;
|
|
final String? fileSystemScheme;
|
|
|
|
/// Whether the build should track widget creation locations.
|
|
final bool trackWidgetCreation;
|
|
|
|
/// Extra command-line options for front-end.
|
|
final List<String> extraFrontEndOptions;
|
|
|
|
/// Extra command-line options for gen_snapshot.
|
|
final List<String> extraGenSnapshotOptions;
|
|
|
|
/// Internal version number (not displayed to users).
|
|
/// Each build must have a unique number to differentiate it from previous builds.
|
|
/// It is used to determine whether one build is more recent than another, with higher numbers indicating more recent build.
|
|
/// On Android it is used as versionCode.
|
|
/// On Xcode builds it is used as CFBundleVersion.
|
|
final String? buildNumber;
|
|
|
|
/// A "x.y.z" string used as the version number shown to users.
|
|
/// For each new version of your app, you will provide a version number to differentiate it from previous versions.
|
|
/// On Android it is used as versionName.
|
|
/// On Xcode builds it is used as CFBundleShortVersionString.
|
|
final String? buildName;
|
|
|
|
/// An optional directory path to save debugging information from dwarf stack
|
|
/// traces. If null, stack trace information is not stripped from the
|
|
/// executable.
|
|
final String? splitDebugInfoPath;
|
|
|
|
/// Whether to apply dart source code obfuscation.
|
|
final bool dartObfuscation;
|
|
|
|
/// An optional path to a JSON containing object SkSL shaders.
|
|
///
|
|
/// Currently this is only supported for Android builds.
|
|
final String? bundleSkSLPath;
|
|
|
|
/// Additional constant values to be made available in the Dart program.
|
|
///
|
|
/// These values can be used with the const `fromEnvironment` constructors of
|
|
/// [bool], [String], [int], and [double].
|
|
final List<String> dartDefines;
|
|
|
|
/// A list of Dart experiments.
|
|
final List<String> dartExperiments;
|
|
|
|
/// The name of a file where flutter assemble will output performance
|
|
/// information in a JSON format.
|
|
///
|
|
/// This is not considered a build input and will not force assemble to
|
|
/// rerun tasks.
|
|
final String? performanceMeasurementFile;
|
|
|
|
/// If provided, an output directory where one or more v8-style heap snapshots
|
|
/// will be written for code size profiling.
|
|
final String? codeSizeDirectory;
|
|
|
|
/// Whether to enable the Gradle daemon when performing an Android build.
|
|
///
|
|
/// Starting the daemon is the default behavior of the gradle wrapper script created
|
|
/// in a Flutter project. Setting this value to false will cause the tool to pass
|
|
/// `--no-daemon` to the gradle wrapper script, preventing it from spawning a daemon
|
|
/// process.
|
|
///
|
|
/// For one-off builds or CI systems, preventing the daemon from spawning will
|
|
/// reduce system resource usage, at the cost of any subsequent builds starting
|
|
/// up slightly slower.
|
|
///
|
|
/// The Gradle daemon may also be disabled in the Android application's properties file.
|
|
final bool androidGradleDaemon;
|
|
|
|
/// Additional key value pairs that are passed directly to the gradle project via the `-P`
|
|
/// flag.
|
|
final List<String> androidProjectArgs;
|
|
|
|
/// The package configuration for the loaded application.
|
|
///
|
|
/// This is captured once during startup, but the actual package configuration
|
|
/// may change during a 'flutter run` workflow.
|
|
final PackageConfig packageConfig;
|
|
|
|
/// The kernel file that the resident compiler will be initialized with.
|
|
///
|
|
/// If this is null, it will be initialized from the default cached location.
|
|
final String? initializeFromDill;
|
|
|
|
/// If set, assumes that the file passed in [initializeFromDill] is up to date
|
|
/// and skips the check and potential invalidation of files.
|
|
final bool assumeInitializeFromDillUpToDate;
|
|
|
|
static const BuildInfo debug = BuildInfo(BuildMode.debug, null, treeShakeIcons: false);
|
|
static const BuildInfo profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
|
|
static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
|
|
static const BuildInfo release = BuildInfo(BuildMode.release, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
|
|
|
|
/// Returns whether a debug build is requested.
|
|
///
|
|
/// Exactly one of [isDebug], [isProfile], or [isRelease] is true.
|
|
bool get isDebug => mode == BuildMode.debug;
|
|
|
|
/// Returns whether a profile build is requested.
|
|
///
|
|
/// Exactly one of [isDebug], [isProfile], [isJitRelease],
|
|
/// or [isRelease] is true.
|
|
bool get isProfile => mode == BuildMode.profile;
|
|
|
|
/// Returns whether a release build is requested.
|
|
///
|
|
/// Exactly one of [isDebug], [isProfile], [isJitRelease],
|
|
/// or [isRelease] is true.
|
|
bool get isRelease => mode == BuildMode.release;
|
|
|
|
/// Returns whether a JIT release build is requested.
|
|
///
|
|
/// Exactly one of [isDebug], [isProfile], [isJitRelease],
|
|
/// or [isRelease] is true.
|
|
bool get isJitRelease => mode == BuildMode.jitRelease;
|
|
|
|
bool get usesAot => isAotBuildMode(mode);
|
|
bool get supportsEmulator => isEmulatorBuildMode(mode);
|
|
bool get supportsSimulator => isEmulatorBuildMode(mode);
|
|
String get modeName => getModeName(mode);
|
|
String get friendlyModeName => getFriendlyModeName(mode);
|
|
|
|
/// the flavor name in the output apk files is lower-cased (see flutter.gradle),
|
|
/// so the lower cased flavor name is used to compute the output file name
|
|
String? get lowerCasedFlavor => flavor?.toLowerCase();
|
|
|
|
/// the flavor name in the output bundle files has the first character lower-cased,
|
|
/// so the uncapitalized flavor name is used to compute the output file name
|
|
String? get uncapitalizedFlavor => _uncapitalize(flavor);
|
|
|
|
/// Convert to a structured string encoded structure appropriate for usage
|
|
/// in build system [Environment.defines].
|
|
///
|
|
/// Fields that are `null` are excluded from this configuration.
|
|
Map<String, String> toBuildSystemEnvironment() {
|
|
// packagesPath and performanceMeasurementFile are not passed into
|
|
// the Environment map.
|
|
return <String, String>{
|
|
kBuildMode: getNameForBuildMode(mode),
|
|
if (dartDefines.isNotEmpty)
|
|
kDartDefines: encodeDartDefines(dartDefines),
|
|
if (dartObfuscation != null)
|
|
kDartObfuscation: dartObfuscation.toString(),
|
|
if (extraFrontEndOptions.isNotEmpty)
|
|
kExtraFrontEndOptions: extraFrontEndOptions.join(','),
|
|
if (extraGenSnapshotOptions.isNotEmpty)
|
|
kExtraGenSnapshotOptions: extraGenSnapshotOptions.join(','),
|
|
if (splitDebugInfoPath != null)
|
|
kSplitDebugInfo: splitDebugInfoPath!,
|
|
if (trackWidgetCreation != null)
|
|
kTrackWidgetCreation: trackWidgetCreation.toString(),
|
|
if (treeShakeIcons != null)
|
|
kIconTreeShakerFlag: treeShakeIcons.toString(),
|
|
if (bundleSkSLPath != null)
|
|
kBundleSkSLPath: bundleSkSLPath!,
|
|
if (codeSizeDirectory != null)
|
|
kCodeSizeDirectory: codeSizeDirectory!,
|
|
if (fileSystemRoots.isNotEmpty)
|
|
kFileSystemRoots: fileSystemRoots.join(','),
|
|
if (fileSystemScheme != null)
|
|
kFileSystemScheme: fileSystemScheme!,
|
|
};
|
|
}
|
|
|
|
/// Convert to a structured string encoded structure appropriate for usage as
|
|
/// environment variables or to embed in other scripts.
|
|
///
|
|
/// Fields that are `null` are excluded from this configuration.
|
|
Map<String, String> toEnvironmentConfig() {
|
|
return <String, String>{
|
|
if (dartDefines.isNotEmpty)
|
|
'DART_DEFINES': encodeDartDefines(dartDefines),
|
|
if (dartObfuscation != null)
|
|
'DART_OBFUSCATION': dartObfuscation.toString(),
|
|
if (extraFrontEndOptions.isNotEmpty)
|
|
'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions.join(','),
|
|
if (extraGenSnapshotOptions.isNotEmpty)
|
|
'EXTRA_GEN_SNAPSHOT_OPTIONS': extraGenSnapshotOptions.join(','),
|
|
if (splitDebugInfoPath != null)
|
|
'SPLIT_DEBUG_INFO': splitDebugInfoPath!,
|
|
if (trackWidgetCreation != null)
|
|
'TRACK_WIDGET_CREATION': trackWidgetCreation.toString(),
|
|
if (treeShakeIcons != null)
|
|
'TREE_SHAKE_ICONS': treeShakeIcons.toString(),
|
|
if (performanceMeasurementFile != null)
|
|
'PERFORMANCE_MEASUREMENT_FILE': performanceMeasurementFile!,
|
|
if (bundleSkSLPath != null)
|
|
'BUNDLE_SKSL_PATH': bundleSkSLPath!,
|
|
if (packagesPath != null)
|
|
'PACKAGE_CONFIG': packagesPath,
|
|
if (codeSizeDirectory != null)
|
|
'CODE_SIZE_DIRECTORY': codeSizeDirectory!,
|
|
};
|
|
}
|
|
|
|
/// Convert this config to a series of project level arguments to be passed
|
|
/// on the command line to gradle.
|
|
List<String> toGradleConfig() {
|
|
// PACKAGE_CONFIG not currently supported.
|
|
return <String>[
|
|
if (dartDefines.isNotEmpty)
|
|
'-Pdart-defines=${encodeDartDefines(dartDefines)}',
|
|
if (dartObfuscation != null)
|
|
'-Pdart-obfuscation=$dartObfuscation',
|
|
if (extraFrontEndOptions.isNotEmpty)
|
|
'-Pextra-front-end-options=${extraFrontEndOptions.join(',')}',
|
|
if (extraGenSnapshotOptions.isNotEmpty)
|
|
'-Pextra-gen-snapshot-options=${extraGenSnapshotOptions.join(',')}',
|
|
if (splitDebugInfoPath != null)
|
|
'-Psplit-debug-info=$splitDebugInfoPath',
|
|
if (trackWidgetCreation != null)
|
|
'-Ptrack-widget-creation=$trackWidgetCreation',
|
|
if (treeShakeIcons != null)
|
|
'-Ptree-shake-icons=$treeShakeIcons',
|
|
if (performanceMeasurementFile != null)
|
|
'-Pperformance-measurement-file=$performanceMeasurementFile',
|
|
if (bundleSkSLPath != null)
|
|
'-Pbundle-sksl-path=$bundleSkSLPath',
|
|
if (codeSizeDirectory != null)
|
|
'-Pcode-size-directory=$codeSizeDirectory',
|
|
for (String projectArg in androidProjectArgs)
|
|
'-P$projectArg',
|
|
];
|
|
}
|
|
}
|
|
|
|
/// Information about an Android build to be performed or used.
|
|
class AndroidBuildInfo {
|
|
const AndroidBuildInfo(
|
|
this.buildInfo, {
|
|
this.targetArchs = const <AndroidArch>[
|
|
AndroidArch.armeabi_v7a,
|
|
AndroidArch.arm64_v8a,
|
|
AndroidArch.x86_64,
|
|
],
|
|
this.splitPerAbi = false,
|
|
this.fastStart = false,
|
|
this.multidexEnabled = false,
|
|
});
|
|
|
|
// The build info containing the mode and flavor.
|
|
final BuildInfo buildInfo;
|
|
|
|
/// Whether to split the shared library per ABI.
|
|
///
|
|
/// When this is false, multiple ABIs will be contained within one primary
|
|
/// build artifact. When this is true, multiple build artifacts (one per ABI)
|
|
/// will be produced.
|
|
final bool splitPerAbi;
|
|
|
|
/// The target platforms for the build.
|
|
final Iterable<AndroidArch> targetArchs;
|
|
|
|
/// Whether to bootstrap an empty application.
|
|
final bool fastStart;
|
|
|
|
/// Whether to enable multidex support for apps with more than 64k methods.
|
|
final bool multidexEnabled;
|
|
}
|
|
|
|
/// A summary of the compilation strategy used for Dart.
|
|
class BuildMode {
|
|
const BuildMode._(this.name);
|
|
|
|
factory BuildMode.fromName(String value) {
|
|
switch (value) {
|
|
case 'debug':
|
|
return BuildMode.debug;
|
|
case 'profile':
|
|
return BuildMode.profile;
|
|
case 'release':
|
|
return BuildMode.release;
|
|
case 'jit_release':
|
|
return BuildMode.jitRelease;
|
|
}
|
|
throw ArgumentError('$value is not a supported build mode');
|
|
}
|
|
|
|
/// Built in JIT mode with no optimizations, enabled asserts, and an observatory.
|
|
static const BuildMode debug = BuildMode._('debug');
|
|
|
|
/// Built in AOT mode with some optimizations and an observatory.
|
|
static const BuildMode profile = BuildMode._('profile');
|
|
|
|
/// Built in AOT mode with all optimizations and no observatory.
|
|
static const BuildMode release = BuildMode._('release');
|
|
|
|
/// Built in JIT mode with all optimizations and no observatory.
|
|
static const BuildMode jitRelease = BuildMode._('jit_release');
|
|
|
|
static const List<BuildMode> values = <BuildMode>[
|
|
debug,
|
|
profile,
|
|
release,
|
|
jitRelease,
|
|
];
|
|
static const Set<BuildMode> releaseModes = <BuildMode>{
|
|
release,
|
|
jitRelease,
|
|
};
|
|
static const Set<BuildMode> jitModes = <BuildMode>{
|
|
debug,
|
|
jitRelease,
|
|
};
|
|
|
|
/// Whether this mode is considered release.
|
|
///
|
|
/// Useful for determining whether we should enable/disable asserts or
|
|
/// other development features.
|
|
bool get isRelease => releaseModes.contains(this);
|
|
|
|
/// Whether this mode is using the JIT runtime.
|
|
bool get isJit => jitModes.contains(this);
|
|
|
|
/// Whether this mode is using the precompiled runtime.
|
|
bool get isPrecompiled => !isJit;
|
|
|
|
/// The name for this build mode.
|
|
final String name;
|
|
|
|
@override
|
|
String toString() => name;
|
|
}
|
|
|
|
/// Return the name for the build mode, or "any" if null.
|
|
String getNameForBuildMode(BuildMode buildMode) {
|
|
return buildMode.name;
|
|
}
|
|
|
|
/// Returns the [BuildMode] for a particular `name`.
|
|
BuildMode getBuildModeForName(String name) {
|
|
return BuildMode.fromName(name);
|
|
}
|
|
|
|
/// Environment type of the target device.
|
|
enum EnvironmentType {
|
|
physical,
|
|
simulator,
|
|
}
|
|
|
|
String? validatedBuildNumberForPlatform(TargetPlatform targetPlatform, String? buildNumber, Logger logger) {
|
|
if (buildNumber == null) {
|
|
return null;
|
|
}
|
|
if (targetPlatform == TargetPlatform.ios ||
|
|
targetPlatform == TargetPlatform.darwin) {
|
|
// See CFBundleVersion at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
|
final RegExp disallowed = RegExp(r'[^\d\.]');
|
|
String tmpBuildNumber = buildNumber.replaceAll(disallowed, '');
|
|
if (tmpBuildNumber.isEmpty) {
|
|
return null;
|
|
}
|
|
final List<String> segments = tmpBuildNumber
|
|
.split('.')
|
|
.where((String segment) => segment.isNotEmpty)
|
|
.toList();
|
|
if (segments.isEmpty) {
|
|
segments.add('0');
|
|
}
|
|
tmpBuildNumber = segments.join('.');
|
|
if (tmpBuildNumber != buildNumber) {
|
|
logger.printTrace('Invalid build-number: $buildNumber for iOS/macOS, overridden by $tmpBuildNumber.\n'
|
|
'See CFBundleVersion at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html');
|
|
}
|
|
return tmpBuildNumber;
|
|
}
|
|
if (targetPlatform == TargetPlatform.android_arm ||
|
|
targetPlatform == TargetPlatform.android_arm64 ||
|
|
targetPlatform == TargetPlatform.android_x64 ||
|
|
targetPlatform == TargetPlatform.android_x86) {
|
|
// See versionCode at https://developer.android.com/studio/publish/versioning
|
|
final RegExp disallowed = RegExp(r'[^\d]');
|
|
String tmpBuildNumberStr = buildNumber.replaceAll(disallowed, '');
|
|
int tmpBuildNumberInt = int.tryParse(tmpBuildNumberStr) ?? 0;
|
|
if (tmpBuildNumberInt < 1) {
|
|
tmpBuildNumberInt = 1;
|
|
}
|
|
tmpBuildNumberStr = tmpBuildNumberInt.toString();
|
|
if (tmpBuildNumberStr != buildNumber) {
|
|
logger.printTrace('Invalid build-number: $buildNumber for Android, overridden by $tmpBuildNumberStr.\n'
|
|
'See versionCode at https://developer.android.com/studio/publish/versioning');
|
|
}
|
|
return tmpBuildNumberStr;
|
|
}
|
|
return buildNumber;
|
|
}
|
|
|
|
String? validatedBuildNameForPlatform(TargetPlatform targetPlatform, String? buildName, Logger logger) {
|
|
if (buildName == null) {
|
|
return null;
|
|
}
|
|
if (targetPlatform == TargetPlatform.ios ||
|
|
targetPlatform == TargetPlatform.darwin) {
|
|
// See CFBundleShortVersionString at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
|
final RegExp disallowed = RegExp(r'[^\d\.]');
|
|
String tmpBuildName = buildName.replaceAll(disallowed, '');
|
|
if (tmpBuildName.isEmpty) {
|
|
return null;
|
|
}
|
|
final List<String> segments = tmpBuildName
|
|
.split('.')
|
|
.where((String segment) => segment.isNotEmpty)
|
|
.toList();
|
|
while (segments.length < 3) {
|
|
segments.add('0');
|
|
}
|
|
tmpBuildName = segments.join('.');
|
|
if (tmpBuildName != buildName) {
|
|
logger.printTrace('Invalid build-name: $buildName for iOS/macOS, overridden by $tmpBuildName.\n'
|
|
'See CFBundleShortVersionString at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html');
|
|
}
|
|
return tmpBuildName;
|
|
}
|
|
if (targetPlatform == TargetPlatform.android ||
|
|
targetPlatform == TargetPlatform.android_arm ||
|
|
targetPlatform == TargetPlatform.android_arm64 ||
|
|
targetPlatform == TargetPlatform.android_x64 ||
|
|
targetPlatform == TargetPlatform.android_x86) {
|
|
// See versionName at https://developer.android.com/studio/publish/versioning
|
|
return buildName;
|
|
}
|
|
return buildName;
|
|
}
|
|
|
|
String getModeName(BuildMode mode) => getEnumName(mode);
|
|
|
|
String getFriendlyModeName(BuildMode mode) {
|
|
return snakeCase(getModeName(mode)).replaceAll('_', ' ');
|
|
}
|
|
|
|
// Returns true if the selected build mode uses ahead-of-time compilation.
|
|
bool isAotBuildMode(BuildMode mode) {
|
|
return mode == BuildMode.profile || mode == BuildMode.release;
|
|
}
|
|
|
|
// Returns true if the given build mode can be used on emulators / simulators.
|
|
bool isEmulatorBuildMode(BuildMode mode) {
|
|
return mode == BuildMode.debug;
|
|
}
|
|
|
|
enum TargetPlatform {
|
|
android,
|
|
ios,
|
|
darwin,
|
|
linux_x64,
|
|
linux_arm64,
|
|
windows_x64,
|
|
windows_uwp_x64,
|
|
fuchsia_arm64,
|
|
fuchsia_x64,
|
|
tester,
|
|
web_javascript,
|
|
// The arch specific android target platforms are soft-deprecated.
|
|
// Instead of using TargetPlatform as a combination arch + platform
|
|
// the code will be updated to carry arch information in [DarwinArch]
|
|
// and [AndroidArch].
|
|
android_arm,
|
|
android_arm64,
|
|
android_x64,
|
|
android_x86,
|
|
}
|
|
|
|
/// iOS and macOS target device architecture.
|
|
//
|
|
// TODO(cbracken): split TargetPlatform.ios into ios_armv7, ios_arm64.
|
|
enum DarwinArch {
|
|
armv7,
|
|
arm64,
|
|
x86_64,
|
|
}
|
|
|
|
// TODO(zanderso): replace all android TargetPlatform usage with AndroidArch.
|
|
enum AndroidArch {
|
|
armeabi_v7a,
|
|
arm64_v8a,
|
|
x86,
|
|
x86_64,
|
|
}
|
|
|
|
/// The default set of iOS device architectures to build for.
|
|
List<DarwinArch> defaultIOSArchsForEnvironment(
|
|
EnvironmentType environmentType) {
|
|
if (environmentType == EnvironmentType.simulator) {
|
|
return <DarwinArch>[
|
|
DarwinArch.x86_64,
|
|
DarwinArch.arm64,
|
|
];
|
|
}
|
|
return <DarwinArch>[
|
|
DarwinArch.armv7,
|
|
DarwinArch.arm64,
|
|
];
|
|
}
|
|
|
|
// Returns the Dart SDK's name for the specified target architecture.
|
|
//
|
|
// When building for Darwin platforms, the tool invokes architecture-specific
|
|
// variants of `gen_snapshot`, one for each target architecture. The output
|
|
// instructions are then built into architecture-specific binaries, which are
|
|
// merged into a universal binary using the `lipo` tool.
|
|
String getDartNameForDarwinArch(DarwinArch arch) {
|
|
switch (arch) {
|
|
case DarwinArch.armv7:
|
|
return 'armv7';
|
|
case DarwinArch.arm64:
|
|
return 'arm64';
|
|
case DarwinArch.x86_64:
|
|
return 'x64';
|
|
}
|
|
}
|
|
|
|
// Returns Apple's name for the specified target architecture.
|
|
//
|
|
// When invoking Apple tools such as `xcodebuild` or `lipo`, the tool often
|
|
// passes one or more target architectures as paramters. The names returned by
|
|
// this function reflect Apple's name for the specified architecture.
|
|
//
|
|
// For consistency with developer expectations, Flutter outputs also use these
|
|
// architecture names in its build products for Darwin target platforms.
|
|
String getNameForDarwinArch(DarwinArch arch) {
|
|
switch (arch) {
|
|
case DarwinArch.armv7:
|
|
return 'armv7';
|
|
case DarwinArch.arm64:
|
|
return 'arm64';
|
|
case DarwinArch.x86_64:
|
|
return 'x86_64';
|
|
}
|
|
}
|
|
|
|
DarwinArch getIOSArchForName(String arch) {
|
|
switch (arch) {
|
|
case 'armv7':
|
|
case 'armv7f': // iPhone 4S.
|
|
case 'armv7s': // iPad 4.
|
|
return DarwinArch.armv7;
|
|
case 'arm64':
|
|
case 'arm64e': // iPhone XS/XS Max/XR and higher. arm64 runs on arm64e devices.
|
|
return DarwinArch.arm64;
|
|
case 'x86_64':
|
|
return DarwinArch.x86_64;
|
|
}
|
|
throw Exception('Unsupported iOS arch name "$arch"');
|
|
}
|
|
|
|
DarwinArch getDarwinArchForName(String arch) {
|
|
switch (arch) {
|
|
case 'arm64':
|
|
return DarwinArch.arm64;
|
|
case 'x86_64':
|
|
return DarwinArch.x86_64;
|
|
}
|
|
throw Exception('Unsupported MacOS arch name "$arch"');
|
|
}
|
|
|
|
String getNameForTargetPlatform(TargetPlatform platform, {DarwinArch? darwinArch}) {
|
|
switch (platform) {
|
|
case TargetPlatform.android_arm:
|
|
return 'android-arm';
|
|
case TargetPlatform.android_arm64:
|
|
return 'android-arm64';
|
|
case TargetPlatform.android_x64:
|
|
return 'android-x64';
|
|
case TargetPlatform.android_x86:
|
|
return 'android-x86';
|
|
case TargetPlatform.ios:
|
|
if (darwinArch != null) {
|
|
return 'ios-${getNameForDarwinArch(darwinArch)}';
|
|
}
|
|
return 'ios';
|
|
case TargetPlatform.darwin:
|
|
if (darwinArch != null) {
|
|
return 'darwin-${getNameForDarwinArch(darwinArch)}';
|
|
}
|
|
return 'darwin';
|
|
case TargetPlatform.linux_x64:
|
|
return 'linux-x64';
|
|
case TargetPlatform.linux_arm64:
|
|
return 'linux-arm64';
|
|
case TargetPlatform.windows_x64:
|
|
return 'windows-x64';
|
|
case TargetPlatform.windows_uwp_x64:
|
|
return 'windows-uwp-x64';
|
|
case TargetPlatform.fuchsia_arm64:
|
|
return 'fuchsia-arm64';
|
|
case TargetPlatform.fuchsia_x64:
|
|
return 'fuchsia-x64';
|
|
case TargetPlatform.tester:
|
|
return 'flutter-tester';
|
|
case TargetPlatform.web_javascript:
|
|
return 'web-javascript';
|
|
case TargetPlatform.android:
|
|
return 'android';
|
|
}
|
|
}
|
|
|
|
TargetPlatform getTargetPlatformForName(String platform) {
|
|
switch (platform) {
|
|
case 'android':
|
|
return TargetPlatform.android;
|
|
case 'android-arm':
|
|
return TargetPlatform.android_arm;
|
|
case 'android-arm64':
|
|
return TargetPlatform.android_arm64;
|
|
case 'android-x64':
|
|
return TargetPlatform.android_x64;
|
|
case 'android-x86':
|
|
return TargetPlatform.android_x86;
|
|
case 'fuchsia-arm64':
|
|
return TargetPlatform.fuchsia_arm64;
|
|
case 'fuchsia-x64':
|
|
return TargetPlatform.fuchsia_x64;
|
|
case 'ios':
|
|
return TargetPlatform.ios;
|
|
case 'darwin':
|
|
// For backward-compatibility and also for Tester, where it must match
|
|
// host platform name (HostPlatform.darwin_x64)
|
|
case 'darwin-x64':
|
|
case 'darwin-arm':
|
|
return TargetPlatform.darwin;
|
|
case 'linux-x64':
|
|
return TargetPlatform.linux_x64;
|
|
case 'linux-arm64':
|
|
return TargetPlatform.linux_arm64;
|
|
case 'windows-x64':
|
|
return TargetPlatform.windows_x64;
|
|
case 'windows-uwp-x64':
|
|
return TargetPlatform.windows_uwp_x64;
|
|
case 'web-javascript':
|
|
return TargetPlatform.web_javascript;
|
|
}
|
|
throw Exception('Unsupported platform name "$platform"');
|
|
}
|
|
|
|
AndroidArch getAndroidArchForName(String platform) {
|
|
switch (platform) {
|
|
case 'android-arm':
|
|
return AndroidArch.armeabi_v7a;
|
|
case 'android-arm64':
|
|
return AndroidArch.arm64_v8a;
|
|
case 'android-x64':
|
|
return AndroidArch.x86_64;
|
|
case 'android-x86':
|
|
return AndroidArch.x86;
|
|
}
|
|
throw Exception('Unsupported Android arch name "$platform"');
|
|
}
|
|
|
|
String getNameForAndroidArch(AndroidArch arch) {
|
|
switch (arch) {
|
|
case AndroidArch.armeabi_v7a:
|
|
return 'armeabi-v7a';
|
|
case AndroidArch.arm64_v8a:
|
|
return 'arm64-v8a';
|
|
case AndroidArch.x86_64:
|
|
return 'x86_64';
|
|
case AndroidArch.x86:
|
|
return 'x86';
|
|
}
|
|
}
|
|
|
|
String getPlatformNameForAndroidArch(AndroidArch arch) {
|
|
switch (arch) {
|
|
case AndroidArch.armeabi_v7a:
|
|
return 'android-arm';
|
|
case AndroidArch.arm64_v8a:
|
|
return 'android-arm64';
|
|
case AndroidArch.x86_64:
|
|
return 'android-x64';
|
|
case AndroidArch.x86:
|
|
return 'android-x86';
|
|
}
|
|
}
|
|
|
|
String fuchsiaArchForTargetPlatform(TargetPlatform targetPlatform) {
|
|
switch (targetPlatform) {
|
|
case TargetPlatform.fuchsia_arm64:
|
|
return 'arm64';
|
|
case TargetPlatform.fuchsia_x64:
|
|
return 'x64';
|
|
case TargetPlatform.android:
|
|
case TargetPlatform.android_arm:
|
|
case TargetPlatform.android_arm64:
|
|
case TargetPlatform.android_x64:
|
|
case TargetPlatform.android_x86:
|
|
case TargetPlatform.darwin:
|
|
case TargetPlatform.ios:
|
|
case TargetPlatform.linux_arm64:
|
|
case TargetPlatform.linux_x64:
|
|
case TargetPlatform.tester:
|
|
case TargetPlatform.web_javascript:
|
|
case TargetPlatform.windows_uwp_x64:
|
|
case TargetPlatform.windows_x64:
|
|
throw UnsupportedError('Unexpected Fuchsia platform $targetPlatform');
|
|
}
|
|
}
|
|
|
|
HostPlatform getCurrentHostPlatform() {
|
|
if (globals.platform.isMacOS) {
|
|
return HostPlatform.darwin_x64;
|
|
}
|
|
if (globals.platform.isLinux) {
|
|
// support x64 and arm64 architecture.
|
|
return globals.os.hostPlatform;
|
|
}
|
|
if (globals.platform.isWindows) {
|
|
return HostPlatform.windows_x64;
|
|
}
|
|
|
|
globals.printWarning('Unsupported host platform, defaulting to Linux');
|
|
|
|
return HostPlatform.linux_x64;
|
|
}
|
|
|
|
/// Returns the top-level build output directory.
|
|
String getBuildDirectory([Config? config, FileSystem? fileSystem]) {
|
|
// TODO(johnmccutchan): Stop calling this function as part of setting
|
|
// up command line argument processing.
|
|
if (context == null) {
|
|
return 'build';
|
|
}
|
|
final Config localConfig = config ?? globals.config;
|
|
final FileSystem localFilesystem = fileSystem ?? globals.fs;
|
|
if (localConfig == null) {
|
|
return 'build';
|
|
}
|
|
|
|
final String buildDir = localConfig.getValue('build-dir') as String? ?? 'build';
|
|
if (localFilesystem.path.isAbsolute(buildDir)) {
|
|
throw Exception(
|
|
'build-dir config setting in ${globals.config.configPath} must be relative');
|
|
}
|
|
return buildDir;
|
|
}
|
|
|
|
/// Returns the Android build output directory.
|
|
String getAndroidBuildDirectory() {
|
|
// TODO(cbracken): move to android subdir.
|
|
return getBuildDirectory();
|
|
}
|
|
|
|
/// Returns the AOT build output directory.
|
|
String getAotBuildDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'aot');
|
|
}
|
|
|
|
/// Returns the asset build output directory.
|
|
String getAssetBuildDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'flutter_assets');
|
|
}
|
|
|
|
/// Returns the iOS build output directory.
|
|
String getIosBuildDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'ios');
|
|
}
|
|
|
|
/// Returns the macOS build output directory.
|
|
String getMacOSBuildDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'macos');
|
|
}
|
|
|
|
/// Returns the web build output directory.
|
|
String getWebBuildDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'web');
|
|
}
|
|
|
|
/// Returns the Linux build output directory.
|
|
String getLinuxBuildDirectory([TargetPlatform? targetPlatform]) {
|
|
final String arch = (targetPlatform == null) ?
|
|
_getCurrentHostPlatformArchName() :
|
|
getNameForTargetPlatformArch(targetPlatform);
|
|
final String subDirs = 'linux/$arch';
|
|
return globals.fs.path.join(getBuildDirectory(), subDirs);
|
|
}
|
|
|
|
/// Returns the Windows build output directory.
|
|
String getWindowsBuildDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'windows');
|
|
}
|
|
|
|
/// Returns the Windows UWP build output directory.
|
|
String getWindowsBuildUwpDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'winuwp');
|
|
}
|
|
|
|
/// Returns the Fuchsia build output directory.
|
|
String getFuchsiaBuildDirectory() {
|
|
return globals.fs.path.join(getBuildDirectory(), 'fuchsia');
|
|
}
|
|
|
|
/// Defines specified via the `--dart-define` command-line option.
|
|
///
|
|
/// These values are URI-encoded and then combined into a comma-separated string.
|
|
const String kDartDefines = 'DartDefines';
|
|
|
|
/// The define to pass a [BuildMode].
|
|
const String kBuildMode = 'BuildMode';
|
|
|
|
/// The define to pass whether we compile 64-bit android-arm code.
|
|
const String kTargetPlatform = 'TargetPlatform';
|
|
|
|
/// The define to control what target file is used.
|
|
const String kTargetFile = 'TargetFile';
|
|
|
|
/// The define to control whether the AOT snapshot is built with bitcode.
|
|
const String kBitcodeFlag = 'EnableBitcode';
|
|
|
|
/// Whether to enable or disable track widget creation.
|
|
const String kTrackWidgetCreation = 'TrackWidgetCreation';
|
|
|
|
/// Additional configuration passed to the dart front end.
|
|
///
|
|
/// This is expected to be a comma separated list of strings.
|
|
const String kExtraFrontEndOptions = 'ExtraFrontEndOptions';
|
|
|
|
/// Additional configuration passed to gen_snapshot.
|
|
///
|
|
/// This is expected to be a comma separated list of strings.
|
|
const String kExtraGenSnapshotOptions = 'ExtraGenSnapshotOptions';
|
|
|
|
/// Whether the build should run gen_snapshot as a split aot build for deferred
|
|
/// components.
|
|
const String kDeferredComponents = 'DeferredComponents';
|
|
|
|
/// Whether to strip source code information out of release builds and where to save it.
|
|
const String kSplitDebugInfo = 'SplitDebugInfo';
|
|
|
|
/// Alternative scheme for file URIs.
|
|
///
|
|
/// May be used along with [kFileSystemRoots] to support a multi-root
|
|
/// filesystem.
|
|
const String kFileSystemScheme = 'FileSystemScheme';
|
|
|
|
/// Additional filesystem roots.
|
|
///
|
|
/// If provided, must be used along with [kFileSystemScheme].
|
|
const String kFileSystemRoots = 'FileSystemRoots';
|
|
|
|
/// The define to control what iOS architectures are built for.
|
|
///
|
|
/// This is expected to be a space-delimited list of architectures. If not
|
|
/// provided, defaults to arm64.
|
|
///
|
|
/// The other supported value is armv7, the 32-bit iOS architecture.
|
|
const String kIosArchs = 'IosArchs';
|
|
|
|
/// The define to control what macOS architectures are built for.
|
|
///
|
|
/// This is expected to be a space-delimited list of architectures. If not
|
|
/// provided, defaults to x86_64.
|
|
///
|
|
/// Supported values are x86_64 and arm64.
|
|
const String kDarwinArchs = 'DarwinArchs';
|
|
|
|
/// Path to the SDK root to be used as the isysroot.
|
|
const String kSdkRoot = 'SdkRoot';
|
|
|
|
/// Whether to enable Dart obfuscation and where to save the symbol map.
|
|
const String kDartObfuscation = 'DartObfuscation';
|
|
|
|
/// An output directory where one or more code-size measurements may be written.
|
|
const String kCodeSizeDirectory = 'CodeSizeDirectory';
|
|
|
|
/// SHA identifier of the Apple developer code signing identity.
|
|
///
|
|
/// Same as EXPANDED_CODE_SIGN_IDENTITY Xcode build setting.
|
|
/// Also discoverable via `security find-identity -p codesigning`.
|
|
const String kCodesignIdentity = 'CodesignIdentity';
|
|
|
|
/// The build define controlling whether icon fonts should be stripped down to
|
|
/// only the glyphs used by the application.
|
|
const String kIconTreeShakerFlag = 'TreeShakeIcons';
|
|
|
|
/// The input key for an SkSL bundle path.
|
|
const String kBundleSkSLPath = 'BundleSkSLPath';
|
|
|
|
final Converter<String, String> _defineEncoder = utf8.encoder.fuse(base64.encoder);
|
|
final Converter<String, String> _defineDecoder = base64.decoder.fuse(utf8.decoder);
|
|
|
|
/// Encode a List of dart defines in a base64 string.
|
|
///
|
|
/// This encoding does not include `,`, which is used to distinguish
|
|
/// the individual entries, nor does it include `%` which is often a
|
|
/// control character on windows command lines.
|
|
///
|
|
/// When decoding this string, it can be safely split on commas, since any
|
|
/// user provided commands will still be encoded.
|
|
///
|
|
/// If the presence of the `/` character ends up being an issue, this can
|
|
/// be changed to use base32 instead.
|
|
String encodeDartDefines(List<String> defines) {
|
|
return defines.map(_defineEncoder.convert).join(',');
|
|
}
|
|
|
|
List<String> decodeCommaSeparated(Map<String, String> environmentDefines, String key) {
|
|
if (!environmentDefines.containsKey(key) || environmentDefines[key]!.isEmpty) {
|
|
return <String>[];
|
|
}
|
|
return environmentDefines[key]!
|
|
.split(',')
|
|
.cast<String>()
|
|
.toList();
|
|
}
|
|
|
|
/// Dart defines are encoded inside [environmentDefines] as a comma-separated list.
|
|
List<String> decodeDartDefines(Map<String, String> environmentDefines, String key) {
|
|
if (!environmentDefines.containsKey(key) || environmentDefines[key]!.isEmpty) {
|
|
return <String>[];
|
|
}
|
|
return environmentDefines[key]!
|
|
.split(',')
|
|
.map<Object>(_defineDecoder.convert)
|
|
.cast<String>()
|
|
.toList();
|
|
}
|
|
|
|
/// The null safety runtime mode the app should be built in.
|
|
enum NullSafetyMode {
|
|
sound,
|
|
unsound,
|
|
/// The null safety mode was not detected. Only supported for 'flutter test'.
|
|
autodetect,
|
|
}
|
|
|
|
String _getCurrentHostPlatformArchName() {
|
|
final HostPlatform hostPlatform = getCurrentHostPlatform();
|
|
return getNameForHostPlatformArch(hostPlatform);
|
|
}
|
|
|
|
String getNameForTargetPlatformArch(TargetPlatform platform) {
|
|
switch (platform) {
|
|
case TargetPlatform.linux_x64:
|
|
case TargetPlatform.darwin:
|
|
case TargetPlatform.windows_x64:
|
|
return 'x64';
|
|
case TargetPlatform.linux_arm64:
|
|
return 'arm64';
|
|
case TargetPlatform.android:
|
|
case TargetPlatform.android_arm:
|
|
case TargetPlatform.android_arm64:
|
|
case TargetPlatform.android_x64:
|
|
case TargetPlatform.android_x86:
|
|
case TargetPlatform.fuchsia_arm64:
|
|
case TargetPlatform.fuchsia_x64:
|
|
case TargetPlatform.ios:
|
|
case TargetPlatform.tester:
|
|
case TargetPlatform.web_javascript:
|
|
case TargetPlatform.windows_uwp_x64:
|
|
throw UnsupportedError('Unexpected target platform $platform');
|
|
}
|
|
}
|
|
|
|
String getNameForHostPlatformArch(HostPlatform platform) {
|
|
switch (platform) {
|
|
case HostPlatform.darwin_x64:
|
|
return 'x64';
|
|
case HostPlatform.darwin_arm:
|
|
return 'arm';
|
|
case HostPlatform.linux_x64:
|
|
return 'x64';
|
|
case HostPlatform.linux_arm64:
|
|
return 'arm64';
|
|
case HostPlatform.windows_x64:
|
|
return 'x64';
|
|
}
|
|
}
|
|
|
|
String? _uncapitalize(String? s) {
|
|
if (s == null || s.isEmpty) {
|
|
return s;
|
|
}
|
|
return s.substring(0, 1).toLowerCase() + s.substring(1);
|
|
}
|