[engine_build_configs] Use dart:ffi Abi to determine the host cpu (flutter/engine#50604)

Follow-up on https://github.com/flutter/engine/pull/50543#discussion_r1486546946
This commit is contained in:
Zachary Anderson 2024-02-13 11:19:27 -08:00 committed by GitHub
parent 5b220df694
commit 9f2ff32022
3 changed files with 74 additions and 24 deletions

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ffi' as ffi;
import 'dart:io' as io;
import 'package:engine_build_configs/engine_build_configs.dart';
@ -119,6 +120,7 @@ The build names are the "name" fields of the maps in the list of "builds".
final GlobalBuildRunner buildRunner = GlobalBuildRunner(
platform: const LocalPlatform(),
processRunner: ProcessRunner(),
abi: ffi.Abi.current(),
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: extraGnArgs,

View File

@ -4,7 +4,8 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io show Directory, Process, ProcessResult;
import 'dart:ffi' as ffi;
import 'dart:io' as io show Directory, Process;
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
@ -111,7 +112,13 @@ typedef RunnerEventHandler = void Function(RunnerEvent);
/// An abstract base clase for running the various tasks that a build config
/// specifies. Derived classes implement the `run()` method.
sealed class Runner {
Runner(this.platform, this.processRunner, this.engineSrcDir, this.dryRun);
Runner(
this.platform,
this.processRunner,
this.abi,
this.engineSrcDir,
this.dryRun,
);
/// Information about the platform that hosts the runner.
final Platform platform;
@ -119,6 +126,9 @@ sealed class Runner {
/// Runs the subprocesses required to run the element of the build config.
final ProcessRunner processRunner;
/// The [Abi] of the host platform.
final ffi.Abi abi;
/// The src/ directory of the engine checkout.
final io.Directory engineSrcDir;
@ -162,6 +172,7 @@ final class GlobalBuildRunner extends Runner {
GlobalBuildRunner({
Platform? platform,
ProcessRunner? processRunner,
ffi.Abi? abi,
required io.Directory engineSrcDir,
required this.build,
this.extraGnArgs = const <String>[],
@ -175,6 +186,7 @@ final class GlobalBuildRunner extends Runner {
}) : super(
platform ?? const LocalPlatform(),
processRunner ?? ProcessRunner(),
abi ?? ffi.Abi.current(),
engineSrcDir,
dryRun,
);
@ -289,22 +301,20 @@ final class GlobalBuildRunner extends Runner {
}
late final String _hostCpu = (){
if (platform.isWindows) {
return platform.environment['PROCESSOR_ARCHITECTURE'] ?? 'x64';
}
final List<String> unameCommand = <String>['uname', '-m'];
final io.ProcessResult unameResult = processRunner.processManager.runSync(
unameCommand,
);
return unameResult.exitCode == 0 ? (unameResult.stdout as String).trim() : 'x64';
return switch (abi) {
ffi.Abi.linuxArm64 || ffi.Abi.macosArm64 || ffi.Abi.windowsArm64 => 'arm64',
ffi.Abi.linuxX64 || ffi.Abi.macosX64 || ffi.Abi.windowsX64 => 'x64',
_ => throw StateError('This host platform "$abi" is not supported.'),
};
}();
late final String _buildtoolsPath = (){
final String platformDir = switch (platform.operatingSystem) {
final String os = platform.operatingSystem;
final String platformDir = switch (os) {
Platform.linux => 'linux-$_hostCpu',
Platform.macOS => 'mac-$_hostCpu',
Platform.windows => 'windows-$_hostCpu',
_ => '<unknown>',
_ => throw StateError('This host OS "$os" is not supported.'),
};
return p.join(engineSrcDir.path, 'buildtools', platformDir);
}();
@ -317,11 +327,12 @@ final class GlobalBuildRunner extends Runner {
final String exe = platform.isWindows ? '.exe' : '';
final String bootstrapPath = p.join(reclientPath, 'bootstrap$exe');
final String reproxyPath = p.join(reclientPath, 'reproxy$exe');
final String reclientConfigFile = switch (platform.operatingSystem) {
final String os = platform.operatingSystem;
final String reclientConfigFile = switch (os) {
Platform.linux => 'reclient-linux.cfg',
Platform.macOS => 'reclient-mac.cfg',
Platform.windows => 'reclient-win.cfg',
_ => '<unknown>',
_ => throw StateError('This host OS "$os" is not supported.'),
};
final String reclientConfigPath = p.join(
engineSrcDir.path, 'flutter', 'build', 'rbe', reclientConfigFile,
@ -487,6 +498,7 @@ final class GlobalBuildRunner extends Runner {
final BuildTaskRunner runner = BuildTaskRunner(
processRunner: processRunner,
platform: platform,
abi: abi,
engineSrcDir: engineSrcDir,
task: task,
dryRun: dryRun,
@ -503,6 +515,7 @@ final class GlobalBuildRunner extends Runner {
final BuildTestRunner runner = BuildTestRunner(
processRunner: processRunner,
platform: platform,
abi: abi,
engineSrcDir: engineSrcDir,
test: test,
extraTestArgs: extraTestArgs,
@ -521,12 +534,14 @@ final class BuildTaskRunner extends Runner {
BuildTaskRunner({
Platform? platform,
ProcessRunner? processRunner,
ffi.Abi? abi,
required io.Directory engineSrcDir,
required this.task,
bool dryRun = false,
}) : super(
platform ?? const LocalPlatform(),
processRunner ?? ProcessRunner(),
abi ?? ffi.Abi.current(),
engineSrcDir,
dryRun,
);
@ -571,6 +586,7 @@ final class BuildTestRunner extends Runner {
BuildTestRunner({
Platform? platform,
ProcessRunner? processRunner,
ffi.Abi? abi,
required io.Directory engineSrcDir,
required this.test,
this.extraTestArgs = const <String>[],
@ -578,6 +594,7 @@ final class BuildTestRunner extends Runner {
}) : super(
platform ?? const LocalPlatform(),
processRunner ?? ProcessRunner(),
abi ?? ffi.Abi.current(),
engineSrcDir,
dryRun,
);

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:convert' as convert;
import 'dart:ffi' as ffi;
import 'dart:io' as io;
import 'package:engine_build_configs/src/build_config.dart';
@ -39,6 +40,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
task: generator,
dryRun: true,
@ -65,6 +67,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
test: test,
dryRun: true,
@ -93,6 +96,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
dryRun: true,
@ -154,6 +158,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: <String>['--extra-gn-arg'],
@ -193,6 +198,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: <String>['--goma'],
@ -218,10 +224,9 @@ void main() {
final GlobalBuildRunner buildRunner = GlobalBuildRunner(
platform: FakePlatform(operatingSystem: Platform.linux),
processRunner: ProcessRunner(
processManager: _fakeProcessManager(
unameResult: io.ProcessResult(1, 0, 'arm64', ''),
),
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: <String>['--rbe'],
@ -263,6 +268,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
runGn: false,
@ -297,6 +303,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
runNinja: false,
@ -338,6 +345,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
runGenerators: false,
@ -381,6 +389,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
runTests: false,
@ -409,10 +418,9 @@ void main() {
final GlobalBuildRunner buildRunner = GlobalBuildRunner(
platform: FakePlatform(operatingSystem: Platform.linux),
processRunner: ProcessRunner(
processManager: _fakeProcessManager(
unameResult: io.ProcessResult(1, 0, 'arm64', ''),
),
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: <String>['--no-lto', '--no-goma', '--rbe'],
@ -448,6 +456,7 @@ void main() {
// dryRun should not try to spawn any processes.
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
dryRun: true,
@ -469,6 +478,7 @@ void main() {
gnResult: io.ProcessResult(1, 1, '', ''),
),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
);
@ -495,6 +505,7 @@ void main() {
ninjaResult: io.ProcessResult(1, 1, '', ''),
),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
);
@ -518,10 +529,10 @@ void main() {
platform: FakePlatform(operatingSystem: Platform.linux),
processRunner: ProcessRunner(
processManager: _fakeProcessManager(
unameResult: io.ProcessResult(1, 0, 'arm64', ''),
bootstrapResult: io.ProcessResult(1, 1, '', ''),
),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: <String>['--rbe'],
@ -547,7 +558,6 @@ void main() {
platform: FakePlatform(operatingSystem: Platform.linux),
processRunner: ProcessRunner(
processManager: _fakeProcessManager(
unameResult: io.ProcessResult(1, 0, 'arm64', ''),
canRun: (Object? exe, {String? workingDirectory}) {
if (exe is String? && exe != null && exe.endsWith('bootstrap')) {
return false;
@ -556,6 +566,7 @@ void main() {
},
),
),
abi: ffi.Abi.linuxX64,
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: <String>['--rbe'],
@ -568,10 +579,31 @@ void main() {
expect(events[2] is RunnerError, isTrue);
});
test('GlobalBuildRunner throws a StateError on an unsupported host cpu', () async {
final GlobalBuild targetBuild = buildConfig.builds[0];
final GlobalBuildRunner buildRunner = GlobalBuildRunner(
platform: FakePlatform(operatingSystem: Platform.linux),
processRunner: ProcessRunner(
processManager: _fakeProcessManager(),
),
abi: ffi.Abi.linuxRiscv32,
engineSrcDir: engine.srcDir,
build: targetBuild,
extraGnArgs: <String>['--rbe'],
);
bool caughtError = false;
try {
await buildRunner.run((RunnerEvent event) {});
} on StateError catch (_) {
caughtError = true;
}
expect(caughtError, isTrue);
});
}
FakeProcessManager _fakeProcessManager({
io.ProcessResult? unameResult,
io.ProcessResult? bootstrapResult,
io.ProcessResult? gnResult,
io.ProcessResult? ninjaResult,
@ -587,7 +619,6 @@ FakeProcessManager _fakeProcessManager({
return FakeProcessManager(
canRun: canRun ?? (Object? exe, {String? workingDirectory}) => true,
onRun: (List<String> cmd) => switch (cmd) {
['uname', ...] => unameResult ?? success,
_ => failUnknown ? io.ProcessResult(1, 1, '', '') : success,
},
onStart: (List<String> cmd) => switch (cmd) {