Unverified Commit 24e06232 authored by stuartmorgan's avatar stuartmorgan Committed by GitHub

Fix local engine use in macOS plugins (#140222)

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 https://github.com/flutter/flutter/issues/132228
parent a7035829
...@@ -84,6 +84,12 @@ class PluginTest { ...@@ -84,6 +84,12 @@ class PluginTest {
section('Test app'); section('Test app');
await app.runFlutterTest(); 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 { } finally {
await plugin.delete(); await plugin.delete();
await app.delete(); await app.delete();
...@@ -95,6 +101,22 @@ class PluginTest { ...@@ -95,6 +101,22 @@ class PluginTest {
rmTree(tempDir); 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 { class _FlutterProject {
...@@ -369,13 +391,28 @@ s.dependency 'AppAuth', '1.6.0' ...@@ -369,13 +391,28 @@ s.dependency 'AppAuth', '1.6.0'
podspec.writeAsStringSync(podspecContent, flush: true); 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 { await inDirectory(Directory(rootPath), () async {
final String buildOutput = await evalFlutter('build', options: <String>[ final String buildOutput = await evalFlutter('build', options: <String>[
target, target,
'-v', '-v',
if (target == 'ios') if (target == 'ios')
'--no-codesign', '--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') { if (target == 'ios' || target == 'macos') {
...@@ -422,6 +459,13 @@ s.dependency 'AppAuth', '1.6.0' ...@@ -422,6 +459,13 @@ s.dependency 'AppAuth', '1.6.0'
throw TaskResult.failure('Transitive dependency build setting MACOSX_DEPLOYMENT_TARGET=10.9 not removed'); 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');
}
}
} }
} }
}); });
......
...@@ -75,6 +75,7 @@ def flutter_additional_ios_build_settings(target) ...@@ -75,6 +75,7 @@ def flutter_additional_ios_build_settings(target)
build_configuration.build_settings['ENABLE_BITCODE'] = 'NO' build_configuration.build_settings['ENABLE_BITCODE'] = 'NO'
# Profile can't be derived from the CocoaPods build configuration. Use release framework (for linking only). # 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 configuration_engine_dir = build_configuration.type == :debug ? debug_framework_dir : release_framework_dir
Dir.new(configuration_engine_dir).each_child do |xcframework_file| Dir.new(configuration_engine_dir).each_child do |xcframework_file|
next if xcframework_file.start_with?('.') # Hidden file, possibly on external disk. next if xcframework_file.start_with?('.') # Hidden file, possibly on external disk.
...@@ -124,6 +125,10 @@ def flutter_additional_macos_build_settings(target) ...@@ -124,6 +125,10 @@ def flutter_additional_macos_build_settings(target)
artifacts_dir = File.join('..', '..', '..', '..', 'bin', 'cache', 'artifacts', 'engine') artifacts_dir = File.join('..', '..', '..', '..', 'bin', 'cache', 'artifacts', 'engine')
debug_framework_dir = File.expand_path(File.join(artifacts_dir, 'darwin-x64'), __FILE__) 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__) 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) unless Dir.exist?(debug_framework_dir)
# macOS artifacts have not been downloaded. # macOS artifacts have not been downloaded.
...@@ -138,7 +143,7 @@ def flutter_additional_macos_build_settings(target) ...@@ -138,7 +143,7 @@ def flutter_additional_macos_build_settings(target)
next unless target.dependencies.any? { |dependency| dependency.name == 'FlutterMacOS' } 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). # 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)" 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. # When deleted, the deployment version will inherit from the higher version derived from the 'Runner' target.
...@@ -319,3 +324,36 @@ def flutter_relative_path_from_podfile(path) ...@@ -319,3 +324,36 @@ def flutter_relative_path_from_podfile(path)
relative = pathname.relative_path_from project_directory_pathname relative = pathname.relative_path_from project_directory_pathname
relative.to_s relative.to_s
end 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
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