Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always embed iOS Flutter.framework build mode version from Xcode configuration #42029

Merged
merged 1 commit into from
Oct 15, 2019

Conversation

jmagman
Copy link
Member

@jmagman jmagman commented Oct 5, 2019

Description

There were two links/copies of Flutter.framework inside my_flutter_app/ios/. One was symlinked from the cache during pod install and embedded by CocoaPods:

if [[ "$CONFIGURATION" == "Debug" ]]; then
  install_framework "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
  install_framework "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework"
fi
if [[ "$CONFIGURATION" == "Profile" ]]; then
  install_framework "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework"
fi

The location of the framework was pulled from FLUTTER_FRAMEWORK_DIR which was only updated from a command line flutter build. This caused issues when a user runs flutter build --debug but then immediately tries to archive their app from Xcode. The FLUTTER_FRAMEWORK_DIR would still point to the Debug version even though the Xcode build config was Release.

The second version is copied during xcode_backend.sh. This used the Xcode build configuration to decide which version of Flutter.framework to use. The script runs on every Xcode build from the command line or from Xcode itself, and correctly (assuming schemes are well-named) chooses the right build mode version of the framework.

The CocoaPods version always gets the last word since it is embedded last after xcode_backend runs. Users experience various production blank screens/crashes/app submission rejections due to accidentally submitting the debug version of their engine.

This change removes the symlinked version and points to the xcode_backend location. It pulls a similar trick as podhelper.rb and copies the FLUTTER_FRAMEWORK_DIR version of the engine to the correct place if xcode_backend has not yet run. This fall-back lets pod install succeed, and the correct version of the framework is always copied over by xcode_backend on every Xcode build/test/archive.

This has the side effect of no longer changing the Podfile.lock (which should be checked into source control) every time the build mode changes.

 EXTERNAL SOURCES:
   Flutter:
    :path: ".symlinks/flutter/ios-release"

becomes

 
 EXTERNAL SOURCES:
   Flutter:
    :path: Flutter

Additionally, the Ruby code was updated to make it more idiomatic.

Related Issues

Fixes #24641
Fixes #25167
Fixes #25370
Fixes #28802

Some of these issues have become muddied so it will fix some people chiming in but not others:
#22765
#24887
#37850

Related:
#36247

Tests

Added a few file location checks to deploy_gallery. It will also be exercised by other integration tests that build or run on iOS.

Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I signed the [CLA].
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

  • Yes, this is a breaking change (Please read [Handling breaking changes]). Replace this with a link to the e-mail where you asked for input on this proposed change.
  • No, this is not a breaking change.

@jmagman jmagman added platform-ios iOS applications specifically tool Affects the "flutter" command-line tool. See also t: labels. t: xcode "xcodebuild" on iOS and general Xcode project management labels Oct 5, 2019
@jmagman jmagman self-assigned this Oct 5, 2019
@fluttergithubbot fluttergithubbot added d: examples Sample code and demos team Infra upgrades, team productivity, code health, technical debt. See also team: labels. customer: gallery Relating to flutter/gallery repository. Please transfer non-framework issues there. labels Oct 5, 2019
@@ -56,7 +56,7 @@ build/
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.*
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Flutter.podspec is now getting copied in. People will have to add it to their .gitignore or check it in... ☹️

puts "Invalid plugin specification: #{line}"
end
end
generated_key_values
end

target 'Runner' do
use_frameworks!
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same file as Podfile-ios-obj except for use_frameworks!.

@@ -1,23 +1,16 @@
PODS:
- Flutter (1.0.0)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be the only Podfile.lock in the entire repo. You can see how it changes.
I don't know why MaterialControls was in there, but it doesn't seem to be a real dependency and was automatically removed?

@@ -15,49 +15,64 @@ def parse_KV_file(file, separator='=')
if !File.exists? file_abs_path
return [];
end
pods_ary = []
generated_key_values = {}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy of the template.

@@ -15,49 +15,64 @@ def parse_KV_file(file, separator='=')
if !File.exists? file_abs_path
return [];
end
pods_ary = []
generated_key_values = {}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy of the template.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these mean this will break for consumers?

Copy link
Member Author

@jmagman jmagman Oct 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the original format of the Generated.xcconfig is the same, and this method isn't accessible outside this file (unless someone was doing some really weird hackery). The method return type is different now, but I changed the two places we call it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I mean the fact that you had to update all of these - will it not be automatic for people running Flutter on existing projects?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only place this method is called is within this Podfile. So if they regenerate the Podfile they will get the updated method and the updated usage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P.S. The stale framework issue this is fixing happens when developers fail to follow the deployment instructions and do not run flutter build ios in release before archiving. So if the Podfile isn't updated, but they follow instructions, they will be fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could read the podfile an dlook for the the offending line?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more detailed message, or a link to some docs on the website, could help folks do the necessary update manually if they have local edits they want to keep. Like, "Your Podfile is out of date. Regenerate it with .... If you have local edits that you'd like to keep, see ... for instructions."

Copy link
Member Author

@jmagman jmagman Oct 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easy to see if their Podfile is out of date without parsing (either .symlinks/flutter exists or it doesn't since that directory gets removed and recreated each pod install run).
The minimum Podfile change (without the mapping refactor) is pretty large, so it's not just a one-line swap:

generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
if generated_xcode_build_settings.empty?
  puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first."
end
generated_xcode_build_settings.map { |p|
  if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
    symlink = File.join('.symlinks', 'flutter')
    File.symlink(File.dirname(p[:path]), symlink)
    pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
  end
}

becomes:

copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
  generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
  if generated_xcode_build_settings.empty?
    puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first."
  end
  generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
  generated_xcode_build_settings.map { |p|
    if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
      cached_framework_dir = p[:path]
      unless File.exist?(copied_framework_path)
        FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
      end
      unless File.exist?(copied_podspec_path)
        FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
      end
    end
  }
end

pod 'Flutter', :path => 'Flutter'

Copy link
Member Author

@jmagman jmagman Oct 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's what it looks like when I have an old version, build, get the migration instructions, remove the Podfile, then build again:
podfile_migration
Note I need to actually add instructions to that issue.

@@ -4,37 +4,75 @@
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

def parse_KV_file(file,separator='=')
project 'Runner', {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy of the template.

@@ -4,56 +4,75 @@
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy of the template.

puts "Invalid plugin specification: #{line}"
end
end
generated_key_values
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return a map of keys => values instead of an array of single key => value maps.


generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually stops the script now instead of just printing, and it's in red (ignore the double period at the end, I fixed that but I was too lazy to generate another screen shot)!
raise

unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is essentially a migration step since people may already have ios/Flutter/Flutter.framework but will definitely not have ios/Flutter/Flutter.podspec.

File.symlink(p[:path], symlink)
pod p[:name], :path => File.join(symlink, 'ios')
}
plugin_pods.each do |name, path|
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more idiomatic Ruby.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take your word for it :)

@codecov
Copy link

codecov bot commented Oct 5, 2019

Codecov Report

Merging #42029 into master will decrease coverage by 0.5%.
The diff coverage is 81.81%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #42029      +/-   ##
==========================================
- Coverage   60.63%   60.13%   -0.51%     
==========================================
  Files         194      194              
  Lines       18886    18897      +11     
==========================================
- Hits        11452    11364      -88     
- Misses       7434     7533      +99
Flag Coverage Δ
#flutter_tool 60.13% <81.81%> (-0.51%) ⬇️
Impacted Files Coverage Δ
...ackages/flutter_tools/lib/src/macos/cocoapods.dart 91.15% <100%> (+0.58%) ⬆️
packages/flutter_tools/lib/src/project.dart 87.5% <50%> (-0.47%) ⬇️
...ges/flutter_tools/lib/src/commands/ide_config.dart 30.39% <0%> (-53.93%) ⬇️
...tter_tools/lib/src/build_system/targets/linux.dart 38.29% <0%> (-46.81%) ⬇️
...tools/lib/src/windows/visual_studio_validator.dart 61.29% <0%> (-32.26%) ⬇️
...utter_tools/lib/src/build_system/build_system.dart 82.72% <0%> (-6.37%) ⬇️
...ages/flutter_tools/lib/src/base/user_messages.dart 47.74% <0%> (-3.61%) ⬇️
packages/flutter_tools/lib/src/base/os.dart 24.6% <0%> (-2.39%) ⬇️
packages/flutter_tools/lib/src/version.dart 93.23% <0%> (-1.94%) ⬇️
packages/flutter_tools/lib/src/cache.dart 48.47% <0%> (-0.71%) ⬇️
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update dda74a1...f3d5013. Read the comment docs.

Copy link
Member

@jonahwilliams jonahwilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What test are we missing that would have caught this? I suppose since the devicelab is building through flutter it wouldn't have tickled this bug at all?


generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flutter packages get or are we rebranding to flutter pub get? Both probably work, not sure if we're consistent with one or the other.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷‍♀ I didn't change the text of this line, I made it a raise instead of a puts. I feel like I've seen flutter packages get in more places, so I'll change it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, flutter packages get is only used in the Podfile-macos (which I didn't change in this PR because I want to test that thoroughly and separately). flutter pub get is used in 26 places in the repo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File.symlink(p[:path], symlink)
pod p[:name], :path => File.join(symlink, 'ios')
}
plugin_pods.each do |name, path|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take your word for it :)

@jmagman
Copy link
Member Author

jmagman commented Oct 5, 2019

What test are we missing that would have caught this? I suppose since the devicelab is building through flutter it wouldn't have tickled this bug at all?

Something like:

flutter build ios --release --no-codesign -t lib/main_publish.dart

but builds with --debug that deploys (uses Release) and then does a smoke test of the archived app.

I'll think more about how to test this...

.gitignore Outdated Show resolved Hide resolved
@dnfield
Copy link
Contributor

dnfield commented Oct 5, 2019

For a test, couldn't we take one of the integration tests we have and assert that the .symlinks folder doesn't have Flutter in it?

@genert
Copy link

genert commented Oct 14, 2019

What is the status of this PR?

@jmagman
Copy link
Member Author

jmagman commented Oct 14, 2019

For a test, couldn't we take one of the integration tests we have and assert that the .symlinks folder doesn't have Flutter in it?

I added a few location checks to deploy_gallery.

@genert
Copy link

genert commented Oct 15, 2019

@jonahwilliams @zanderso

@jmagman jmagman merged commit 357d02c into flutter:master Oct 15, 2019
@jmagman jmagman deleted the pod-path branch October 15, 2019 22:34
Inconnu08 pushed a commit to Inconnu08/flutter that referenced this pull request Nov 26, 2019
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 3, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
customer: gallery Relating to flutter/gallery repository. Please transfer non-framework issues there. d: examples Sample code and demos platform-ios iOS applications specifically t: xcode "xcodebuild" on iOS and general Xcode project management team Infra upgrades, team productivity, code health, technical debt. See also team: labels. tool Affects the "flutter" command-line tool. See also t: labels.
Projects
None yet
7 participants