diff --git a/dev/devicelab/lib/framework/ios.dart b/dev/devicelab/lib/framework/ios.dart new file mode 100644 index 0000000000..9b5145cecf --- /dev/null +++ b/dev/devicelab/lib/framework/ios.dart @@ -0,0 +1,82 @@ +// Copyright (c) 2017 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 'dart:async'; +import 'dart:io' as dart_io; + +import 'package:file/file.dart'; +import 'package:file/local.dart' as io; +import 'package:path/path.dart' as path; + +const String _kProvisioningConfigFileEnvironmentVariable = 'FLUTTER_DEVICELAB_XCODE_PROVISIONING_CONFIG'; +const String _kTestXcconfigFileName = 'TestConfig.xcconfig'; +const FileSystem _fs = const io.LocalFileSystem(); + +/// Patches the given Xcode project adding provisioning certificates and team +/// information required to build and run the project. +Future prepareProvisioningCertificates(String flutterProjectPath) async { + final String certificateConfig = await _readProvisioningConfigFile(); + await _patchFlutterXcconfigIfNotPatched(flutterProjectPath); + + final File testXcconfig = _fs.file(path.join(flutterProjectPath, 'ios/Flutter/$_kTestXcconfigFileName')); + await testXcconfig.writeAsString(certificateConfig); +} + +Future _patchFlutterXcconfigIfNotPatched(String flutterProjectPath) async { + final File flutterXcconfig = _fs.file(path.join(flutterProjectPath, 'ios/Flutter/Flutter.xcconfig')); + + if (!(await flutterXcconfig.exists())) { + throw 'File not found: ${flutterXcconfig.path}'; + } + + const String include = '#include "$_kTestXcconfigFileName"'; + final String contents = await flutterXcconfig.readAsString(); + if (!contents.contains(include)) { + final IOSink patchOut = flutterXcconfig.openWrite(mode: FileMode.APPEND); + patchOut.writeln(include); + await patchOut.close(); + } +} + +Future _readProvisioningConfigFile() async { + void throwUsageError(String specificMessage) { + throw ''' +================================================================================ +You are attempting to build an XCode project, which requires a provisioning +certificate and team information. The test framework attempted to locate an +.xcconfig file whose path is defined by the environment variable +$_kProvisioningConfigFileEnvironmentVariable. + +$specificMessage +================================================================================ +'''.trim(); + } + + if (!dart_io.Platform.environment.containsKey(_kProvisioningConfigFileEnvironmentVariable)) { + throwUsageError(''' +$_kProvisioningConfigFileEnvironmentVariable variable is not defined in your +environment. Please, define it and try again. + +Example provisioining xcconfig: + +ProvisioningStyle=Manual +CODE_SIGN_IDENTITY=... +PROVISIONING_PROFILE=... +DEVELOPMENT_TEAM=... +PROVISIONING_PROFILE_SPECIFIER=... +'''.trim()); + } + + final String filePath = dart_io.Platform.environment[_kProvisioningConfigFileEnvironmentVariable]; + final File file = _fs.file(filePath); + if (!(await file.exists())) { + throwUsageError(''' +File not found: $filePath + +It is defined by environment variable $_kProvisioningConfigFileEnvironmentVariable +'''.trim()); + } + + return file.readAsString(); +} diff --git a/dev/devicelab/lib/tasks/gallery.dart b/dev/devicelab/lib/tasks/gallery.dart index 0dcd6b006e..6a36e00e6a 100644 --- a/dev/devicelab/lib/tasks/gallery.dart +++ b/dev/devicelab/lib/tasks/gallery.dart @@ -9,6 +9,7 @@ import 'dart:math' as math; import '../framework/adb.dart'; import '../framework/framework.dart'; +import '../framework/ios.dart'; import '../framework/utils.dart'; TaskFunction createGalleryTransitionTest() { @@ -27,6 +28,7 @@ class GalleryTransitionTest { await flutter('packages', options: ['get']); if (deviceOperatingSystem == DeviceOperatingSystem.ios) { + await prepareProvisioningCertificates(galleryDirectory.path); // This causes an Xcode project to be created. await flutter('build', options: ['ios', '--profile']); } diff --git a/dev/devicelab/lib/tasks/microbenchmarks.dart b/dev/devicelab/lib/tasks/microbenchmarks.dart index 20106aa0e8..2cb0515eb2 100644 --- a/dev/devicelab/lib/tasks/microbenchmarks.dart +++ b/dev/devicelab/lib/tasks/microbenchmarks.dart @@ -10,6 +10,7 @@ import 'package:path/path.dart' as path; import 'package:flutter_devicelab/framework/adb.dart'; import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/framework/ios.dart'; import 'package:flutter_devicelab/framework/utils.dart'; /// Creates a device lab task that runs benchmarks in @@ -23,6 +24,9 @@ TaskFunction createMicrobenchmarkTask() { print('Running $benchmarkPath'); final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/benchmarks/microbenchmarks')); final Process flutterProcess = await inDirectory(appDir, () async { + if (deviceOperatingSystem == DeviceOperatingSystem.ios) { + await prepareProvisioningCertificates(appDir.path); + } return await _startFlutter( options: [ '--profile', // --release doesn't work on iOS due to code signing issues diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart index 0481e587e9..4d73d77cee 100644 --- a/dev/devicelab/lib/tasks/perf_tests.dart +++ b/dev/devicelab/lib/tasks/perf_tests.dart @@ -7,6 +7,7 @@ import 'dart:convert' show JSON; import '../framework/adb.dart'; import '../framework/framework.dart'; +import '../framework/ios.dart'; import '../framework/utils.dart'; @@ -97,6 +98,7 @@ class StartupTest { await flutter('packages', options: ['get']); if (deviceOperatingSystem == DeviceOperatingSystem.ios) { + await prepareProvisioningCertificates(testDirectory); // This causes an Xcode project to be created. await flutter('build', options: ['ios', '--profile']); } @@ -137,6 +139,7 @@ class PerfTest { await flutter('packages', options: ['get']); if (deviceOperatingSystem == DeviceOperatingSystem.ios) { + await prepareProvisioningCertificates(testDirectory); // This causes an Xcode project to be created. await flutter('build', options: ['ios', '--profile']); } @@ -187,6 +190,7 @@ class DriverTest { await flutter('packages', options: ['get']); if (deviceOperatingSystem == DeviceOperatingSystem.ios) { + await prepareProvisioningCertificates(testDirectory); // This causes an Xcode project to be created. await flutter('build', options: ['ios', '--profile']); } @@ -275,6 +279,7 @@ class MemoryTest { await flutter('packages', options: ['get']); if (deviceOperatingSystem == DeviceOperatingSystem.ios) { + await prepareProvisioningCertificates(testDirectory); // This causes an Xcode project to be created. await flutter('build', options: ['ios', '--profile']); } diff --git a/dev/devicelab/lib/tasks/size_tests.dart b/dev/devicelab/lib/tasks/size_tests.dart index c9ca9a87c8..7678ac0078 100644 --- a/dev/devicelab/lib/tasks/size_tests.dart +++ b/dev/devicelab/lib/tasks/size_tests.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:flutter_devicelab/framework/adb.dart'; import '../framework/framework.dart'; +import '../framework/ios.dart'; import '../framework/utils.dart'; TaskFunction createBasicMaterialAppSizeTest() { @@ -30,6 +31,7 @@ TaskFunction createBasicMaterialAppSizeTest() { await flutter('build', options: ['clean']); if (deviceOperatingSystem == DeviceOperatingSystem.ios) { + await prepareProvisioningCertificates(sampleDir.path); await flutter('build', options: ['ios', '--release']); // IPAs are created manually AFAICT await exec('tar', ['-zcf', 'build/app.ipa', 'build/ios/Release-iphoneos/Runner.app/']);