mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

macOS 14 added new requirements that un-codesigned sandbox apps must be granted access when changed. Waiting for this UI caused macOS tests to fail on macOS 14 because the test runner forced codesigning off. Additionally, adding codesigning is not sufficient, since it must still be approved before codesigning is enough to pass the check. As a workaround, this PR disables sandboxing for macOS apps/tests in CI.  https://developer.apple.com/documentation/updates/security#June-2023) > App Sandbox now associates your macOS app with its sandbox container using its code signature. The operating system asks the person using your app to grant permission if it tries to access a sandbox container associated with a different app. For more information, see [Accessing files from the macOS App Sandbox](https://developer.apple.com/documentation/security/app_sandbox/accessing_files_from_the_macos_app_sandbox). And that link explains why this is happening on a macOS 14 update: > In macOS 14 and later, the operating system uses your appâs code signature to associate it with its sandbox container. If your app tries to access the sandbox container owned by another app, the system asks the person using your app whether to grant access. If the person denies access and your app is already running, then it canât read or write the files in the other appâs sandbox container. If the person denies access while your app is launching and trying to enter the other appâs sandbox container, your app fails to launch. > > The operating system also tracks the association between an appâs code signing identity and its sandbox container for helper tools, including launch agents. If a person denies permission for a launch agent to enter its sandbox container and the app fails to start, launchd starts the launch agent again and the operating system re-requests access. Fixes https://github.com/flutter/flutter/issues/149268. Fixes framework part of https://github.com/flutter/flutter/issues/149264. Might fix packages issue: https://github.com/flutter/flutter/issues/149329. Verified framework tests: https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20plugin_test_macos/9/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20run_debug_test_macos/2/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20tool_integration_tests_4_4/2/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20integration_ui_test_test_macos/3/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20flavors_test_macos/3/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac_benchmark%20complex_layout_scroll_perf_macos__timeline_summary/6/overview
512 lines
18 KiB
Dart
512 lines
18 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 'dart:async';
|
|
|
|
import 'package:fake_async/fake_async.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/application_package.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/os.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/desktop_device.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
|
import 'package:flutter_tools/src/macos/macos_device.dart';
|
|
import 'package:flutter_tools/src/project.dart';
|
|
|
|
import 'package:test/fake.dart';
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
|
|
void main() {
|
|
group('Basic info', () {
|
|
testWithoutContext('Category is desktop', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(device.category, Category.desktop);
|
|
});
|
|
|
|
testWithoutContext('Not an emulator', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(await device.isLocalEmulator, false);
|
|
expect(await device.emulatorId, null);
|
|
});
|
|
|
|
testWithoutContext('Uses OS name as SDK name', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(await device.sdkNameAndVersion, 'Example');
|
|
});
|
|
});
|
|
|
|
group('Install', () {
|
|
testWithoutContext('Install checks always return true', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(await device.isAppInstalled(FakeApplicationPackage()), true);
|
|
expect(await device.isLatestBuildInstalled(FakeApplicationPackage()), true);
|
|
expect(device.category, Category.desktop);
|
|
});
|
|
|
|
testWithoutContext('Install and uninstall are no-ops that report success', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
|
|
expect(await device.uninstallApp(package), true);
|
|
expect(await device.isAppInstalled(package), true);
|
|
expect(await device.isLatestBuildInstalled(package), true);
|
|
|
|
expect(await device.installApp(package), true);
|
|
expect(await device.isAppInstalled(package), true);
|
|
expect(await device.isLatestBuildInstalled(package), true);
|
|
expect(device.category, Category.desktop);
|
|
});
|
|
});
|
|
|
|
group('Starting and stopping application', () {
|
|
testWithoutContext('Stop without start is a successful no-op', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
|
|
expect(await device.stopApp(package), true);
|
|
});
|
|
|
|
testWithoutContext('Can run from prebuilt application', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager, fileSystem: fileSystem);
|
|
final String? executableName = device.executablePathForDevice(FakeApplicationPackage(), BuildInfo.debug);
|
|
fileSystem.file(executableName).writeAsStringSync('\n');
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
expect(result.vmServiceUri, Uri.parse('http://127.0.0.1/0'));
|
|
});
|
|
|
|
testWithoutContext('Null executable path fails gracefully', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final DesktopDevice device = setUpDesktopDevice(nullExecutablePathForDevice: true, logger: logger);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
);
|
|
|
|
expect(result.started, false);
|
|
expect(logger.errorText, contains('Unable to find executable to run'));
|
|
});
|
|
|
|
testWithoutContext('stopApp kills process started by startApp', () async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
expect(await device.stopApp(package), true);
|
|
});
|
|
});
|
|
|
|
testWithoutContext('startApp supports DebuggingOptions through FLUTTER_ENGINE_SWITCH environment variables', () async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
environment: const <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'trace-startup=true',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'enable-software-rendering=true',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'skia-deterministic-rendering=true',
|
|
'FLUTTER_ENGINE_SWITCH_5': 'trace-skia=true',
|
|
'FLUTTER_ENGINE_SWITCH_6': 'trace-allowlist=foo,bar',
|
|
'FLUTTER_ENGINE_SWITCH_7': 'trace-skia-allowlist=skia.a,skia.b',
|
|
'FLUTTER_ENGINE_SWITCH_8': 'trace-systrace=true',
|
|
'FLUTTER_ENGINE_SWITCH_9': 'trace-to-file=path/to/trace.binpb',
|
|
'FLUTTER_ENGINE_SWITCH_10': 'endless-trace-buffer=true',
|
|
'FLUTTER_ENGINE_SWITCH_11': 'dump-skp-on-shader-compilation=true',
|
|
'FLUTTER_ENGINE_SWITCH_12': 'cache-sksl=true',
|
|
'FLUTTER_ENGINE_SWITCH_13': 'purge-persistent-cache=true',
|
|
'FLUTTER_ENGINE_SWITCH_14': 'enable-impeller=false',
|
|
'FLUTTER_ENGINE_SWITCH_15': 'enable-checked-mode=true',
|
|
'FLUTTER_ENGINE_SWITCH_16': 'verify-entry-points=true',
|
|
'FLUTTER_ENGINE_SWITCH_17': 'start-paused=true',
|
|
'FLUTTER_ENGINE_SWITCH_18': 'disable-service-auth-codes=true',
|
|
'FLUTTER_ENGINE_SWITCH_19': 'dart-flags=--null_assertions',
|
|
'FLUTTER_ENGINE_SWITCH_20': 'use-test-fonts=true',
|
|
'FLUTTER_ENGINE_SWITCH_21': 'verbose-logging=true',
|
|
'FLUTTER_ENGINE_SWITCHES': '21',
|
|
}
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
platformArgs: <String, Object>{
|
|
'trace-startup': true,
|
|
},
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
startPaused: true,
|
|
disableServiceAuthCodes: true,
|
|
enableSoftwareRendering: true,
|
|
skiaDeterministicRendering: true,
|
|
traceSkia: true,
|
|
traceAllowlist: 'foo,bar',
|
|
traceSkiaAllowlist: 'skia.a,skia.b',
|
|
traceSystrace: true,
|
|
traceToFile: 'path/to/trace.binpb',
|
|
endlessTraceBuffer: true,
|
|
dumpSkpOnShaderCompilation: true,
|
|
cacheSkSL: true,
|
|
purgePersistentCache: true,
|
|
useTestFonts: true,
|
|
verboseSystemLogs: true,
|
|
nullAssertions: true,
|
|
),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
});
|
|
|
|
testWithoutContext('startApp supports DebuggingOptions through FLUTTER_ENGINE_SWITCH environment variables when debugging is disabled', () async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
environment: const <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'trace-startup=true',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'trace-allowlist=foo,bar',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'cache-sksl=true',
|
|
'FLUTTER_ENGINE_SWITCH_5': 'enable-impeller=false',
|
|
'FLUTTER_ENGINE_SWITCHES': '5',
|
|
}
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
platformArgs: <String, Object>{
|
|
'trace-startup': true,
|
|
},
|
|
debuggingOptions: DebuggingOptions.disabled(
|
|
BuildInfo.debug,
|
|
traceAllowlist: 'foo,bar',
|
|
cacheSkSL: true,
|
|
),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
});
|
|
|
|
testWithoutContext('Port forwarder is a no-op', () async {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
final DevicePortForwarder portForwarder = device.portForwarder;
|
|
final int result = await portForwarder.forward(2);
|
|
|
|
expect(result, 2);
|
|
expect(portForwarder.forwardedPorts.isEmpty, true);
|
|
});
|
|
|
|
testWithoutContext('createDevFSWriter returns a LocalDevFSWriter', () {
|
|
final FakeDesktopDevice device = setUpDesktopDevice();
|
|
|
|
expect(device.createDevFSWriter(FakeApplicationPackage(), ''), isA<LocalDevFSWriter>());
|
|
});
|
|
|
|
testWithoutContext('startApp supports dartEntrypointArgs', () async {
|
|
final Completer<void> completer = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug', 'arg1', 'arg2'],
|
|
stdout: 'The Dart VM service is listening on http://127.0.0.1/0\n',
|
|
completer: completer,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(processManager: processManager);
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
final LaunchResult result = await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
dartEntrypointArgs: <String>['arg1', 'arg2'],
|
|
),
|
|
);
|
|
|
|
expect(result.started, true);
|
|
});
|
|
|
|
testWithoutContext('Device logger captures all output', () async {
|
|
final Completer<void> exitCompleter = Completer<void>();
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
FakeCommand(
|
|
command: const <String>['debug', 'arg1', 'arg2'],
|
|
exitCode: -1,
|
|
stderr: 'Oops\n',
|
|
completer: exitCompleter,
|
|
outputFollowsExit: true,
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(
|
|
processManager: processManager,
|
|
);
|
|
unawaited(Future<void>(() {
|
|
exitCompleter.complete();
|
|
}));
|
|
|
|
// Start looking for 'Oops' in the stream before starting the app.
|
|
expect(device.getLogReader().logLines, emits('Oops'));
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
dartEntrypointArgs: <String>['arg1', 'arg2'],
|
|
),
|
|
);
|
|
});
|
|
|
|
testWithoutContext('Desktop devices pass through the enable-impeller flag', () async {
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
const FakeCommand(
|
|
command: <String>['debug'],
|
|
exitCode: -1,
|
|
environment: <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'enable-impeller=true',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'enable-checked-mode=true',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'verify-entry-points=true',
|
|
'FLUTTER_ENGINE_SWITCHES': '4'
|
|
}
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(
|
|
processManager: processManager,
|
|
);
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
enableImpeller: ImpellerStatus.enabled,
|
|
dartEntrypointArgs: <String>[],
|
|
),
|
|
);
|
|
});
|
|
|
|
testWithoutContext('Desktop devices pass through the --no-enable-impeller flag', () async {
|
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
const FakeCommand(
|
|
command: <String>['debug'],
|
|
exitCode: -1,
|
|
environment: <String, String>{
|
|
'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true',
|
|
'FLUTTER_ENGINE_SWITCH_2': 'enable-impeller=false',
|
|
'FLUTTER_ENGINE_SWITCH_3': 'enable-checked-mode=true',
|
|
'FLUTTER_ENGINE_SWITCH_4': 'verify-entry-points=true',
|
|
'FLUTTER_ENGINE_SWITCHES': '4'
|
|
}
|
|
),
|
|
]);
|
|
final FakeDesktopDevice device = setUpDesktopDevice(
|
|
processManager: processManager,
|
|
);
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
await device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
enableImpeller: ImpellerStatus.disabled,
|
|
dartEntrypointArgs: <String>[],
|
|
),
|
|
);
|
|
});
|
|
|
|
testUsingContext('macOS devices print warning if Dart VM not found within timeframe in CI', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final FakeMacOSDevice device = FakeMacOSDevice(
|
|
fileSystem: MemoryFileSystem.test(),
|
|
processManager: FakeProcessManager.any(),
|
|
operatingSystemUtils: FakeOperatingSystemUtils(),
|
|
logger: logger,
|
|
);
|
|
|
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
|
|
|
FakeAsync().run((FakeAsync fakeAsync) {
|
|
device.startApp(
|
|
package,
|
|
prebuiltApplication: true,
|
|
debuggingOptions: DebuggingOptions.enabled(
|
|
BuildInfo.debug,
|
|
enableImpeller: ImpellerStatus.disabled,
|
|
dartEntrypointArgs: <String>[],
|
|
usingCISystem: true,
|
|
),
|
|
);
|
|
fakeAsync.flushTimers();
|
|
expect(logger.errorText, contains('Ensure sandboxing is disabled by checking the set CODE_SIGN_ENTITLEMENTS'));
|
|
});
|
|
});
|
|
}
|
|
|
|
FakeDesktopDevice setUpDesktopDevice({
|
|
FileSystem? fileSystem,
|
|
Logger? logger,
|
|
ProcessManager? processManager,
|
|
OperatingSystemUtils? operatingSystemUtils,
|
|
bool nullExecutablePathForDevice = false,
|
|
}) {
|
|
return FakeDesktopDevice(
|
|
fileSystem: fileSystem ?? MemoryFileSystem.test(),
|
|
logger: logger ?? BufferLogger.test(),
|
|
processManager: processManager ?? FakeProcessManager.any(),
|
|
operatingSystemUtils: operatingSystemUtils ?? FakeOperatingSystemUtils(),
|
|
nullExecutablePathForDevice: nullExecutablePathForDevice,
|
|
);
|
|
}
|
|
|
|
/// A trivial subclass of DesktopDevice for testing the shared functionality.
|
|
class FakeDesktopDevice extends DesktopDevice {
|
|
FakeDesktopDevice({
|
|
required ProcessManager processManager,
|
|
required Logger logger,
|
|
required FileSystem fileSystem,
|
|
required OperatingSystemUtils operatingSystemUtils,
|
|
this.nullExecutablePathForDevice = false,
|
|
}) : super(
|
|
'dummy',
|
|
platformType: PlatformType.linux,
|
|
ephemeral: false,
|
|
processManager: processManager,
|
|
logger: logger,
|
|
fileSystem: fileSystem,
|
|
operatingSystemUtils: operatingSystemUtils,
|
|
);
|
|
|
|
/// The [mainPath] last passed to [buildForDevice].
|
|
String? lastBuiltMainPath;
|
|
|
|
/// The [buildInfo] last passed to [buildForDevice].
|
|
BuildInfo? lastBuildInfo;
|
|
|
|
final bool nullExecutablePathForDevice;
|
|
|
|
@override
|
|
String get name => 'dummy';
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.tester;
|
|
|
|
@override
|
|
bool isSupported() => true;
|
|
|
|
@override
|
|
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
|
|
|
@override
|
|
Future<void> buildForDevice({
|
|
String? mainPath,
|
|
BuildInfo? buildInfo,
|
|
bool usingCISystem = false,
|
|
}) async {
|
|
lastBuiltMainPath = mainPath;
|
|
lastBuildInfo = buildInfo;
|
|
}
|
|
|
|
// Dummy implementation that just returns the build mode name.
|
|
@override
|
|
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) {
|
|
if (nullExecutablePathForDevice) {
|
|
return null;
|
|
}
|
|
return buildInfo.mode.cliName;
|
|
}
|
|
}
|
|
|
|
class FakeApplicationPackage extends Fake implements ApplicationPackage { }
|
|
class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
|
|
@override
|
|
String get name => 'Example';
|
|
}
|
|
|
|
class FakeMacOSDevice extends MacOSDevice {
|
|
FakeMacOSDevice({
|
|
required super.processManager,
|
|
required super.logger,
|
|
required super.fileSystem,
|
|
required super.operatingSystemUtils,
|
|
});
|
|
|
|
@override
|
|
String get name => 'dummy';
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.tester;
|
|
|
|
@override
|
|
bool isSupported() => true;
|
|
|
|
@override
|
|
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
|
|
|
@override
|
|
Future<void> buildForDevice({
|
|
String? mainPath,
|
|
BuildInfo? buildInfo,
|
|
bool usingCISystem = false,
|
|
}) async {
|
|
}
|
|
|
|
// Dummy implementation that just returns the build mode name.
|
|
@override
|
|
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) {
|
|
return buildInfo.mode.cliName;
|
|
}
|
|
}
|