Commit c65e9d19 authored by Ralph Bergmann's avatar Ralph Bergmann Committed by Todd Volkert

add version to pubspec.yaml (#16857)

Uses the `version` property from the `pubspec.yaml` file to set the corresponding fields in the `local.properties` file respectively in the `Generated.xcconfig` file.

The `--build-name` and `--build-number` options have changed. Now they trump the `version` property from the `pubspec.yaml` file.

If the `version` property is not set and the  `--build-name` and `--build-number` options are not provided, the build command will not change the `local.properties` / `Generated.xcconfig` file.
parent f9860480
......@@ -3,9 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:meta/meta.dart';
import '../android/android_sdk.dart';
import '../artifacts.dart';
......@@ -17,7 +14,9 @@ import '../base/platform.dart';
import '../base/process.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../bundle.dart' as bundle;
import '../cache.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
......@@ -95,7 +94,7 @@ Future<GradleProject> _gradleProject() async {
// of calculating the app properties using Gradle. This may take minutes.
Future<GradleProject> _readGradleProject() async {
final String gradle = await _ensureGradle();
updateLocalProperties();
await updateLocalProperties();
try {
final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true);
final RunResult runResult = await runCheckedAsync(
......@@ -198,10 +197,13 @@ distributionUrl=https\\://services.gradle.org/distributions/gradle-$gradleVersio
}
/// Create android/local.properties if needed, and update Flutter settings.
void updateLocalProperties({String projectPath, BuildInfo buildInfo}) {
Future<void> updateLocalProperties({String projectPath, BuildInfo buildInfo}) async {
final File localProperties = (projectPath == null)
? fs.file(fs.path.join('android', 'local.properties'))
: fs.file(fs.path.join(projectPath, 'android', 'local.properties'));
final String flutterManifest = (projectPath == null)
? fs.path.join(bundle.defaultManifestPath)
: fs.path.join(projectPath, bundle.defaultManifestPath);
bool changed = false;
SettingsFile settings;
......@@ -225,85 +227,39 @@ void updateLocalProperties({String projectPath, BuildInfo buildInfo}) {
changed = true;
}
if (changed)
settings.writeContents(localProperties);
}
Future<Null> findAndReplaceVersionProperties({@required BuildInfo buildInfo}) {
assert(buildInfo != null, 'buildInfo can\'t be null');
final Completer<Null> completer = new Completer<Null>();
// early return, if nothing has to be changed
if (buildInfo.buildNumber == null && buildInfo.buildName == null) {
completer.complete();
return completer.future;
FlutterManifest manifest;
try {
manifest = await FlutterManifest.createFromPath(flutterManifest);
} catch (error) {
throwToolExit('Failed to load pubspec.yaml: $error');
}
final File appGradle = fs.file(fs.path.join('android', 'app', 'build.gradle'));
final File appGradleTmp = fs.file(fs.path.join('android', 'app', 'build.gradle.tmp'));
appGradleTmp.createSync();
if (appGradle.existsSync() && appGradleTmp.existsSync()) {
final Stream<List<int>> inputStream = appGradle.openRead();
final IOSink sink = appGradleTmp.openWrite();
inputStream.transform(utf8.decoder)
.transform(const LineSplitter())
.map((String line) {
// find and replace build number
if (buildInfo.buildNumber != null) {
if (line.contains(new RegExp(r'^[ |\t]*(versionCode)[ =\t]*\d*'))) {
return line.splitMapJoin(new RegExp(r'(versionCode)[ =\t]*\d*'), onMatch: (Match m) {
return 'versionCode ${buildInfo.buildNumber}';
});
}
}
final String buildName = buildInfo?.buildName ?? manifest.buildName;
if (buildName != null) {
settings.values['flutter.versionName'] = buildName;
changed = true;
}
// find and replace build name
if (buildInfo.buildName != null) {
if (line.contains(new RegExp(r'^[ |\t]*(versionName)[ =\t]*\"[0-9.]*"'))) {
return line.splitMapJoin(new RegExp(r'(versionName)[ =\t]*\"[0-9.]*"'), onMatch: (Match m) {
return 'versionName "${buildInfo.buildName}"';
});
}
}
return line;
})
.listen((String line) {
sink.writeln(line);
},
onDone: () {
sink.close();
try {
final File gradleOld = appGradle.renameSync(fs.path.join('android', 'app', 'build.gradle.old'));
appGradleTmp.renameSync(fs.path.join('android', 'app', 'build.gradle'));
gradleOld.deleteSync();
completer.complete();
} catch (error) {
printError('Failed to change version properties. $error');
completer.completeError('Failed to change version properties. $error');
}
},
onError: (dynamic error, StackTrace stack) {
printError('Failed to change version properties. ${error.toString()}');
sink.close();
appGradleTmp.deleteSync();
completer.completeError('Failed to change version properties. ${error.toString()}', stack);
},
);
final int buildNumber = buildInfo?.buildNumber ?? manifest.buildNumber;
if (buildNumber != null) {
settings.values['flutter.versionCode'] = '$buildNumber';
changed = true;
}
return completer.future;
if (changed)
settings.writeContents(localProperties);
}
Future<Null> buildGradleProject(BuildInfo buildInfo, String target) async {
// Update the local.properties file with the build mode.
// Update the local.properties file with the build mode, version name and code.
// FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2
// uses the standard Android way to determine what to build, but we still
// update local.properties, in case we want to use it in the future.
updateLocalProperties(buildInfo: buildInfo);
await findAndReplaceVersionProperties(buildInfo: buildInfo);
// Version name and number are provided by the pubspec.yaml file
// and can be overwritten with flutter build command.
// The default Gradle script reads the version name and number
// from the local.properties file.
await updateLocalProperties(buildInfo: buildInfo);
final String gradle = await _ensureGradle();
......
......@@ -289,11 +289,11 @@ To edit platform code in an IDE see https://flutter.io/platform-plugins/#edit-co
if (argResults['pub']) {
await pubGet(context: PubContext.create, directory: appPath, offline: argResults['offline']);
new FlutterProject(fs.directory(appPath)).ensureReadyForPlatformSpecificTooling();
await new FlutterProject(fs.directory(appPath)).ensureReadyForPlatformSpecificTooling();
}
if (android_sdk.androidSdk != null)
gradle.updateLocalProperties(projectPath: appPath);
await gradle.updateLocalProperties(projectPath: appPath);
return generatedCount;
}
......
......@@ -82,13 +82,13 @@ class PackagesGetCommand extends FlutterCommand {
await _runPubGet(target);
final FlutterProject rootProject = new FlutterProject(fs.directory(target));
rootProject.ensureReadyForPlatformSpecificTooling();
await rootProject.ensureReadyForPlatformSpecificTooling();
// Get/upgrade packages in example app as well
if (rootProject.hasExampleApp) {
final FlutterProject exampleProject = rootProject.example;
await _runPubGet(exampleProject.directory.path);
exampleProject.ensureReadyForPlatformSpecificTooling();
await exampleProject.ensureReadyForPlatformSpecificTooling();
}
}
}
......
......@@ -13,6 +13,8 @@ import 'base/file_system.dart';
import 'cache.dart';
import 'globals.dart';
final RegExp _versionPattern = new RegExp(r'^(\d+)(\.(\d+)(\.(\d+))?)?(\+(\d+))?$');
/// A wrapper around the `flutter` section in the `pubspec.yaml` file.
class FlutterManifest {
FlutterManifest._();
......@@ -51,6 +53,36 @@ class FlutterManifest {
String get appName => _descriptor['name'] ?? '';
/// The version String from the `pubspec.yaml` file.
/// Can be null if it isn't set or has a wrong format.
String get appVersion {
final String version = _descriptor['version']?.toString();
if (version != null && _versionPattern.hasMatch(version))
return version;
else
return null;
}
/// The build version name from the `pubspec.yaml` file.
/// Can be null if version isn't set or has a wrong format.
String get buildName {
if (appVersion != null && appVersion.contains('+'))
return appVersion.split('+')?.elementAt(0);
else
return appVersion;
}
/// The build version number from the `pubspec.yaml` file.
/// Can be null if version isn't set or has a wrong format.
int get buildNumber {
if (appVersion != null && appVersion.contains('+')) {
final String value = appVersion.split('+')?.elementAt(1);
return value == null ? null : int.tryParse(value);
} else {
return null;
}
}
bool get usesMaterialDesign {
return _flutterDescriptor['uses-material-design'] ?? false;
}
......
......@@ -242,7 +242,7 @@ Future<XcodeBuildResult> buildXcodeProject({
final Directory appDirectory = fs.directory(app.appDirectory);
await _addServicesToBundle(appDirectory);
updateGeneratedXcodeProperties(
await updateGeneratedXcodeProperties(
projectPath: fs.currentDirectory.path,
buildInfo: buildInfo,
targetOverride: targetOverride,
......@@ -272,53 +272,6 @@ Future<XcodeBuildResult> buildXcodeProject({
await fingerprinter.writeFingerprint();
}
// If buildNumber is not specified, keep the project untouched.
if (buildInfo.buildNumber != null) {
final Status buildNumberStatus =
logger.startProgress('Setting CFBundleVersion...', expectSlowOperation: true);
try {
final RunResult buildNumberResult = await runAsync(
<String>[
'/usr/bin/env',
'xcrun',
'agvtool',
'new-version',
'-all',
buildInfo.buildNumber.toString(),
],
workingDirectory: app.appDirectory,
);
if (buildNumberResult.exitCode != 0) {
throwToolExit('Xcode failed to set new version\n${buildNumberResult.stderr}');
}
} finally {
buildNumberStatus.stop();
}
}
// If buildName is not specified, keep the project untouched.
if (buildInfo.buildName != null) {
final Status buildNameStatus =
logger.startProgress('Setting CFBundleShortVersionString...', expectSlowOperation: true);
try {
final RunResult buildNameResult = await runAsync(
<String>[
'/usr/bin/env',
'xcrun',
'agvtool',
'new-marketing-version',
buildInfo.buildName,
],
workingDirectory: app.appDirectory,
);
if (buildNameResult.exitCode != 0) {
throwToolExit('Xcode failed to set new marketing version\n${buildNameResult.stderr}');
}
} finally {
buildNameStatus.stop();
}
}
final List<String> buildCommands = <String>[
'/usr/bin/env',
'xcrun',
......
......@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:meta/meta.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart';
......@@ -15,6 +18,7 @@ import '../base/utils.dart';
import '../build_info.dart';
import '../bundle.dart' as bundle;
import '../cache.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
final RegExp _settingExpr = new RegExp(r'(\w+)\s*=\s*(.*)$');
......@@ -30,11 +34,11 @@ String _generatedXcodePropertiesPath(String projectPath) {
/// Writes default Xcode properties files in the Flutter project at [projectPath],
/// if project is an iOS project and such files do not already exist.
void generateXcodeProperties(String projectPath) {
Future<void> generateXcodeProperties(String projectPath) async {
if (fs.isDirectorySync(fs.path.join(projectPath, 'ios'))) {
if (fs.file(_generatedXcodePropertiesPath(projectPath)).existsSync())
return;
updateGeneratedXcodeProperties(
await updateGeneratedXcodeProperties(
projectPath: projectPath,
buildInfo: BuildInfo.debug,
targetOverride: bundle.defaultMainPath,
......@@ -47,12 +51,12 @@ void generateXcodeProperties(String projectPath) {
///
/// targetOverride: Optional parameter, if null or unspecified the default value
/// from xcode_backend.sh is used 'lib/main.dart'.
void updateGeneratedXcodeProperties({
Future<void> updateGeneratedXcodeProperties({
@required String projectPath,
@required BuildInfo buildInfo,
String targetOverride,
@required bool previewDart2,
}) {
}) async {
final StringBuffer localsBuffer = new StringBuffer();
localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
......@@ -77,6 +81,24 @@ void updateGeneratedXcodeProperties({
localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');
final String flutterManifest = fs.path.join(projectPath, bundle.defaultManifestPath);
FlutterManifest manifest;
try {
manifest = await FlutterManifest.createFromPath(flutterManifest);
} catch (error) {
throwToolExit('Failed to load pubspec.yaml: $error');
}
final String buildName = buildInfo?.buildName ?? manifest.buildName;
if (buildName != null) {
localsBuffer.writeln('FLUTTER_BUILD_NAME=$buildName');
}
final int buildNumber = buildInfo?.buildNumber ?? manifest.buildNumber;
if (buildNumber != null) {
localsBuffer.writeln('FLUTTER_BUILD_NUMBER=$buildNumber');
}
if (artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = artifacts;
localsBuffer.writeln('LOCAL_ENGINE=${localEngineArtifacts.engineOutPath}');
......
......@@ -55,12 +55,12 @@ class FlutterProject {
/// Generates project files necessary to make Gradle builds work on Android
/// and CocoaPods+Xcode work on iOS, for app projects only
void ensureReadyForPlatformSpecificTooling() {
Future<void> ensureReadyForPlatformSpecificTooling() async {
if (!directory.existsSync() || hasExampleApp) {
return;
}
injectPlugins(directory: directory.path);
generateXcodeProperties(directory.path);
await generateXcodeProperties(directory.path);
}
}
......
......@@ -329,7 +329,7 @@ abstract class FlutterCommand extends Command<Null> {
if (shouldRunPub) {
await pubGet(context: PubContext.getVerifyContext(name));
new FlutterProject(fs.currentDirectory).ensureReadyForPlatformSpecificTooling();
await new FlutterProject(fs.currentDirectory).ensureReadyForPlatformSpecificTooling();
}
setupApplicationPackages();
......
......@@ -11,6 +11,16 @@ if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
throw new GradleException("versionCode not found. Define flutter.versionCode in the local.properties file.")
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
throw new GradleException("versionName not found. Define flutter.versionName in the local.properties file.")
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
......@@ -26,8 +36,8 @@ android {
applicationId "{{androidIdentifier}}"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
......
......@@ -11,6 +11,16 @@ if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
throw new GradleException("versionCode not found. Define flutter.versionCode in the local.properties file.")
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
throw new GradleException("versionName not found. Define flutter.versionName in the local.properties file.")
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
......@@ -31,8 +41,8 @@ android {
applicationId "{{androidIdentifier}}"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
......
......@@ -370,7 +370,7 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
......@@ -393,7 +393,7 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
......
......@@ -368,7 +368,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
......@@ -396,7 +396,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
......
......@@ -15,11 +15,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
......
name: {{projectName}}
description: {{description}}
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# Read more about versioning at semver.org.
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
......
......@@ -2,16 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter_tools/src/android/gradle.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:test/test.dart';
import '../src/common.dart';
import '../src/context.dart';
void main() {
group('gradle build', () {
test('do not crash if there is no Android SDK', () {
test('do not crash if there is no Android SDK', () async {
Exception shouldBeToolExit;
try {
// We'd like to always set androidSdk to null and test updateLocalProperties. But that's
......@@ -21,7 +25,7 @@ void main() {
// This test is written to fail if our bots get Android SDKs in the future: shouldBeToolExit
// will be null and our expectation would fail. That would remind us to make these tests
// hermetic before adding Android SDKs to the bots.
updateLocalProperties();
await updateLocalProperties();
} on Exception catch (e) {
shouldBeToolExit = e;
}
......@@ -126,4 +130,181 @@ someOtherProperty: someOtherValue
expect(project.assembleTaskFor(const BuildInfo(BuildMode.release, 'unknown')), isNull);
});
});
group('Gradle local.properties', () {
Directory temp;
setUp(() {
Cache.disableLocking();
temp = fs.systemTempDirectory.createTempSync('flutter_tools');
});
tearDown(() {
temp.deleteSync(recursive: true);
});
Future<String> createMinimalProject(String manifest) async {
final Directory directory = temp.childDirectory('android_project');
final File manifestFile = directory.childFile('pubspec.yaml');
manifestFile.createSync(recursive: true);
manifestFile.writeAsStringSync(manifest);
return directory.path;
}
String propertyFor(String key, File file) {
return file
.readAsLinesSync()
.where((String line) => line.startsWith('$key='))
.map((String line) => line.split('=')[1])
.first;
}
Future<void> checkBuildVersion({
String manifest,
BuildInfo buildInfo,
String expectedBuildName,
String expectedBuildNumber,
}) async {
final String projectPath = await createMinimalProject(manifest);
try {
await updateLocalProperties(projectPath: projectPath, buildInfo: buildInfo);
final String propertiesPath = fs.path.join(projectPath, 'android', 'local.properties');
final File localPropertiesFile = fs.file(propertiesPath);
expect(propertyFor('flutter.versionName', localPropertiesFile), expectedBuildName);
expect(propertyFor('flutter.versionCode', localPropertiesFile), expectedBuildNumber);
} on Exception {
// Android SDK not found, skip test
}
}
testUsingContext('extract build name and number from pubspec.yaml', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.0',
expectedBuildNumber: '1',
);
});
testUsingContext('extract build name from pubspec.yaml', () async {
const String manifest = '''
name: test
version: 1.0.0
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.0',
expectedBuildNumber: null,
);
});
testUsingContext('allow build info to override build name', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2');
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '1',
);
});
testUsingContext('allow build info to override build number', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.0',
expectedBuildNumber: '3',
);
});
testUsingContext('allow build info to override build name and number', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '3',
);
});
testUsingContext('allow build info to override build name and set number', () async {
const String manifest = '''
name: test
version: 1.0.0
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '3',
);
});
testUsingContext('allow build info to set build name and number', () async {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '3',
);
});
});
}
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -359,6 +361,118 @@ flutter:
final FlutterManifest flutterManifest = await FlutterManifest.createFromString(manifest);
expect(flutterManifest.isEmpty, false);
});
Future<void> checkManifestVersion({
String manifest,
String expectedAppVersion,
String expectedBuildName,
int expectedBuildNumber,
}) async {
final FlutterManifest flutterManifest = await FlutterManifest.createFromString(manifest);
expect(flutterManifest.appVersion, expectedAppVersion);
expect(flutterManifest.buildName, expectedBuildName);
expect(flutterManifest.buildNumber, expectedBuildNumber);
}
test('parses major.minor.patch+build version clause', () async {
const String manifest = '''
name: test
version: 1.0.0+2
dependencies:
flutter:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '1.0.0+2',
expectedBuildName: '1.0.0',
expectedBuildNumber: 2,
);
});
test('parses major.minor+build version clause', () async {
const String manifest = '''
name: test
version: 1.0+2
dependencies:
flutter:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '1.0+2',
expectedBuildName: '1.0',
expectedBuildNumber: 2,
);
});
test('parses major+build version clause', () async {
const String manifest = '''
name: test
version: 1+2
dependencies:
flutter:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '1+2',
expectedBuildName: '1',
expectedBuildNumber: 2,
);
});
test('parses major version clause', () async {
const String manifest = '''
name: test
version: 1
dependencies:
flutter:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '1',
expectedBuildName: '1',
expectedBuildNumber: null,
);
});
test('parses empty version clause', () async {
const String manifest = '''
name: test
version:
dependencies:
flutter:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: null,
expectedBuildName: null,
expectedBuildNumber: null,
);
});
test('parses no version clause', () async {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: null,
expectedBuildName: null,
expectedBuildNumber: null,
);
});
});
group('FlutterManifest with MemoryFileSystem', () {
......@@ -371,8 +485,7 @@ dependencies:
flutter:
''';
final FlutterManifest flutterManifest = await FlutterManifest
.createFromString(manifest);
final FlutterManifest flutterManifest = await FlutterManifest.createFromString(manifest);
expect(flutterManifest.isEmpty, false);
}
......
......@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/bundle.dart' as bundle;
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
......@@ -277,14 +281,14 @@ Information about project "Runner":
});
}
testUsingOsxContext('sets ARCHS=armv7 when armv7 local engine is set', () {
testUsingOsxContext('sets ARCHS=armv7 when armv7 local engine is set', () async {
when(mockArtifacts.getArtifactPath(Artifact.flutterFramework, TargetPlatform.ios, any)).thenReturn('engine');
when(mockArtifacts.engineOutPath).thenReturn(fs.path.join('out', 'ios_profile_arm'));
const BuildInfo buildInfo = const BuildInfo(BuildMode.debug, null,
previewDart2: true,
targetPlatform: TargetPlatform.ios,
);
updateGeneratedXcodeProperties(
await updateGeneratedXcodeProperties(
projectPath: 'path/to/project',
buildInfo: buildInfo,
previewDart2: true,
......@@ -297,14 +301,14 @@ Information about project "Runner":
expect(contents.contains('ARCHS=armv7'), isTrue);
});
testUsingOsxContext('sets ARCHS=armv7 when armv7 local engine is set', () {
testUsingOsxContext('sets ARCHS=armv7 when armv7 local engine is set', () async {
when(mockArtifacts.getArtifactPath(Artifact.flutterFramework, TargetPlatform.ios, any)).thenReturn('engine');
when(mockArtifacts.engineOutPath).thenReturn(fs.path.join('out', 'ios_profile'));
const BuildInfo buildInfo = const BuildInfo(BuildMode.debug, null,
previewDart2: true,
targetPlatform: TargetPlatform.ios,
);
updateGeneratedXcodeProperties(
await updateGeneratedXcodeProperties(
projectPath: 'path/to/project',
buildInfo: buildInfo,
previewDart2: true,
......@@ -317,6 +321,185 @@ Information about project "Runner":
expect(contents.contains('ARCHS=arm64'), isTrue);
});
});
group('Xcode Generated.xcconfig', () {
Directory temp;
setUp(() {
Cache.disableLocking();
temp = fs.systemTempDirectory.createTempSync('flutter_tools');
});
tearDown(() {
temp.deleteSync(recursive: true);
});
Future<String> createMinimalProject(String manifest) async {
final Directory directory = temp.childDirectory('ios_project');
final File manifestFile = directory.childFile('pubspec.yaml');
manifestFile.createSync(recursive: true);
manifestFile.writeAsStringSync(manifest);
return directory.path;
}
String propertyFor(String key, File file) {
final List<String> properties = file
.readAsLinesSync()
.where((String line) => line.startsWith('$key='))
.map((String line) => line.split('=')[1])
.toList();
return properties.isEmpty ? null : properties.first;
}
Future<void> checkBuildVersion({
String manifest,
BuildInfo buildInfo,
String expectedBuildName,
String expectedBuildNumber,
}) async {
final String projectPath = await createMinimalProject(manifest);
await updateGeneratedXcodeProperties(
projectPath: projectPath,
buildInfo: buildInfo,
targetOverride: bundle.defaultMainPath,
previewDart2: false,
);
final String propertiesPath = fs.path.join(projectPath, 'ios', 'Flutter', 'Generated.xcconfig');
final File localPropertiesFile = fs.file(propertiesPath);
expect(propertyFor('FLUTTER_BUILD_NAME', localPropertiesFile), expectedBuildName);
expect(propertyFor('FLUTTER_BUILD_NUMBER', localPropertiesFile), expectedBuildNumber);
}
testUsingContext('extract build name and number from pubspec.yaml', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.0',
expectedBuildNumber: '1',
);
});
testUsingContext('extract build name from pubspec.yaml', () async {
const String manifest = '''
name: test
version: 1.0.0
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.0',
expectedBuildNumber: null,
);
});
testUsingContext('allow build info to override build name', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2');
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '1',
);
});
testUsingContext('allow build info to override build number', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.0',
expectedBuildNumber: '3',
);
});
testUsingContext('allow build info to override build name and number', () async {
const String manifest = '''
name: test
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '3',
);
});
testUsingContext('allow build info to override build name and set number', () async {
const String manifest = '''
name: test
version: 1.0.0
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '3',
);
});
testUsingContext('allow build info to set build name and number', () async {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
''';
const BuildInfo buildInfo = const BuildInfo(BuildMode.release, null, buildName: '1.0.2', buildNumber: 3);
await checkBuildVersion(
manifest: manifest,
buildInfo: buildInfo,
expectedBuildName: '1.0.2',
expectedBuildNumber: '3',
);
});
});
}
Platform fakePlatform(String name) {
......
......@@ -20,23 +20,23 @@ void main() {
group('ensure ready for platform-specific tooling', () {
testInMemory('does nothing, if project is not created', () async {
final FlutterProject project = someProject();
project.ensureReadyForPlatformSpecificTooling();
await project.ensureReadyForPlatformSpecificTooling();
expect(project.directory.existsSync(), isFalse);
});
testInMemory('does nothing in plugin or package root project', () async {
final FlutterProject project = aPluginProject();
project.ensureReadyForPlatformSpecificTooling();
await project.ensureReadyForPlatformSpecificTooling();
expect(project.ios.directory.childFile('Runner/GeneratedPluginRegistrant.h').existsSync(), isFalse);
expect(project.ios.directory.childFile('Flutter/Generated.xcconfig').existsSync(), isFalse);
});
testInMemory('injects plugins', () async {
final FlutterProject project = aProjectWithIos();
project.ensureReadyForPlatformSpecificTooling();
await project.ensureReadyForPlatformSpecificTooling();
expect(project.ios.directory.childFile('Runner/GeneratedPluginRegistrant.h').existsSync(), isTrue);
});
testInMemory('generates Xcode configuration', () async {
final FlutterProject project = aProjectWithIos();
project.ensureReadyForPlatformSpecificTooling();
await project.ensureReadyForPlatformSpecificTooling();
expect(project.ios.directory.childFile('Flutter/Generated.xcconfig').existsSync(), isTrue);
});
});
......
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