Skip to content

Commit

Permalink
Fix local engine use in macOS plugins (flutter#140222)
Browse files Browse the repository at this point in the history
Currently podhelper.rb will always point plugin builds at the cached engine artifacts, even when using `--local-engine`. In most cases this is fine, since when the final build actually runs it will be using the engine bundled into the app build, which will be the correct local engine build. When trying to test a local engine build with API additions against a local plugin modified to use those additions to ensure that they are working as expected, however, compilation will fail, because the new APIs won't be present in the plugin build.

This fixes that for macOS, and adds a TODO for iOS (which is more complicated to fix due to the host vs target build distinction).

macOS portion of flutter#132228
  • Loading branch information
stuartmorgan committed Jan 4, 2024
1 parent a703582 commit 24e0623
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 2 deletions.
46 changes: 45 additions & 1 deletion dev/devicelab/lib/tasks/plugin_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class PluginTest {
section('Test app');
await app.runFlutterTest();
}
// Validate local engine handling. Currently only implemented for macOS.
if (!dartOnlyPlugin) {
section('Validate local engine configuration');
final String fakeEngineSourcePath = path.join(tempDir.path, 'engine');
await _testLocalEngineConfiguration(app, fakeEngineSourcePath);
}
} finally {
await plugin.delete();
await app.delete();
Expand All @@ -95,6 +101,22 @@ class PluginTest {
rmTree(tempDir);
}
}

Future<void> _testLocalEngineConfiguration(_FlutterProject app, String fakeEngineSourcePath) async {
// The tool requires that a directory that looks like an engine build
// actually exists when passing --local-engine, so create a fake skeleton.
final Directory buildDir = Directory(path.join(fakeEngineSourcePath, 'out', 'foo'));
buildDir.createSync(recursive: true);
// Currently this test is only implemented for macOS; it can be extended to
// others as needed.
if (buildTarget == 'macos') {
// Clean before regenerating the config to ensure that the pod steps run.
await inDirectory(Directory(app.rootPath), () async {
await evalFlutter('clean');
});
await app.build(buildTarget, configOnly: true, localEngine: buildDir);
}
}
}

class _FlutterProject {
Expand Down Expand Up @@ -369,13 +391,28 @@ s.dependency 'AppAuth', '1.6.0'
podspec.writeAsStringSync(podspecContent, flush: true);
}

Future<void> build(String target, {bool validateNativeBuildProject = true}) async {
Future<void> build(
String target, {
bool validateNativeBuildProject = true,
bool configOnly = false,
Directory? localEngine,
}) async {
await inDirectory(Directory(rootPath), () async {
final String buildOutput = await evalFlutter('build', options: <String>[
target,
'-v',
if (target == 'ios')
'--no-codesign',
if (configOnly)
'--config-only',
if (localEngine != null)
// The engine directory is of the form <fake-source-path>/out/<fakename>,
// which has to be broken up into the component flags.
...<String>[
'--local-engine-src-path=${localEngine.parent.parent.path}',
'--local-engine=${path.basename(localEngine.path)}',
'--local-engine-host=${path.basename(localEngine.path)}',
]
]);

if (target == 'ios' || target == 'macos') {
Expand Down Expand Up @@ -422,6 +459,13 @@ s.dependency 'AppAuth', '1.6.0'
throw TaskResult.failure('Transitive dependency build setting MACOSX_DEPLOYMENT_TARGET=10.9 not removed');
}
}

if (localEngine != null) {
final RegExp localEngineSearchPath = RegExp('FRAMEWORK_SEARCH_PATHS\\s*=[^;]*${localEngine.path}');
if (!localEngineSearchPath.hasMatch(podsProjectContent)) {
throw TaskResult.failure('FRAMEWORK_SEARCH_PATHS does not contain the --local-engine path');
}
}
}
}
});
Expand Down
40 changes: 39 additions & 1 deletion packages/flutter_tools/bin/podhelper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def flutter_additional_ios_build_settings(target)
build_configuration.build_settings['ENABLE_BITCODE'] = 'NO'

# Profile can't be derived from the CocoaPods build configuration. Use release framework (for linking only).
# TODO(stuartmorgan): Handle local engines here; see https://github.com/flutter/flutter/issues/132228
configuration_engine_dir = build_configuration.type == :debug ? debug_framework_dir : release_framework_dir
Dir.new(configuration_engine_dir).each_child do |xcframework_file|
next if xcframework_file.start_with?('.') # Hidden file, possibly on external disk.
Expand Down Expand Up @@ -124,6 +125,10 @@ def flutter_additional_macos_build_settings(target)
artifacts_dir = File.join('..', '..', '..', '..', 'bin', 'cache', 'artifacts', 'engine')
debug_framework_dir = File.expand_path(File.join(artifacts_dir, 'darwin-x64'), __FILE__)
release_framework_dir = File.expand_path(File.join(artifacts_dir, 'darwin-x64-release'), __FILE__)
application_path = File.dirname(defined_in_file.realpath) if respond_to?(:defined_in_file)
# Find the local engine path, if any.
local_engine = application_path.nil? ?
nil : flutter_get_local_engine_dir(File.join(application_path, 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'))

unless Dir.exist?(debug_framework_dir)
# macOS artifacts have not been downloaded.
Expand All @@ -138,7 +143,7 @@ def flutter_additional_macos_build_settings(target)
next unless target.dependencies.any? { |dependency| dependency.name == 'FlutterMacOS' }

# Profile can't be derived from the CocoaPods build configuration. Use release framework (for linking only).
configuration_engine_dir = build_configuration.type == :debug ? debug_framework_dir : release_framework_dir
configuration_engine_dir = local_engine || (build_configuration.type == :debug ? debug_framework_dir : release_framework_dir)
build_configuration.build_settings['FRAMEWORK_SEARCH_PATHS'] = "\"#{configuration_engine_dir}\" $(inherited)"

# When deleted, the deployment version will inherit from the higher version derived from the 'Runner' target.
Expand Down Expand Up @@ -319,3 +324,36 @@ def flutter_relative_path_from_podfile(path)
relative = pathname.relative_path_from project_directory_pathname
relative.to_s
end

def flutter_parse_xcconfig_file(file)
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
entries = Hash.new
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) { |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
key_value_pair = line.split(pattern = '=')
if key_value_pair.length == 2
entries[key_value_pair[0].strip()] = key_value_pair[1].strip();
else
puts "Invalid key/value pair: #{line}"
end
}
return entries
end

def flutter_get_local_engine_dir(xcconfig_file)
file_abs_path = File.expand_path(xcconfig_file)
if !File.exists? file_abs_path
return nil
end
config = flutter_parse_xcconfig_file(xcconfig_file)
local_engine = config['LOCAL_ENGINE']
base_dir = config['FLUTTER_ENGINE']
if !local_engine.nil? && !base_dir.nil?
return File.join(base_dir, 'out', local_engine)
end
return nil
end

0 comments on commit 24e0623

Please sign in to comment.