Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Postmortem for switching Lyft's iOS app to Bazel

Keith Smiley
December 11, 2019

Postmortem for switching Lyft's iOS app to Bazel

Details of how we migrated Lyft's iOS codebase to build entirely with bazel presented at BazelCon 2019

Keith Smiley

December 11, 2019
Tweet

More Decks by Keith Smiley

Other Decks in Technology

Transcript

  1. Postmortem for switching
    Lyft's iOS app to Bazel

    View full-size slide

  2. Lyft
    - 70+ iOS engineers
    - 2 iOS tooling engineers
    - 800k+ lines of Swift, 0 lines of Objective-C
    - 5500+ bazel targets

    View full-size slide

  3. Standard iOS tooling

    View full-size slide

  4. Scalable, flexible, open
    source

    View full-size slide

  5. Shared tooling

    View full-size slide

  6. Remote caching / execution

    View full-size slide

  7. How we migrated

    View full-size slide

  8. 1. Lyft.xcodeproj

    View full-size slide

  9. 2. Reign in your build

    View full-size slide

  10. 4. Ramp up with bazel

    View full-size slide

  11. 5. Prototype your build

    View full-size slide

  12. 6. Build it for real

    View full-size slide

  13. 7. Some IDE integration

    View full-size slide

  14. 8. Documentation

    View full-size slide

  15. What went well

    View full-size slide

  16. Iterating with starlark

    View full-size slide

  17. Simple project means simpler
    migration

    View full-size slide

  18. Staying up to date

    View full-size slide

  19. Keeping up with the
    community

    View full-size slide

  20. What could have gone better

    View full-size slide

  21. Disclaimer: bazel is great

    View full-size slide

  22. Adoption cost is very high

    View full-size slide

  23. Google's defaults aren't
    always right for the iOS
    community

    View full-size slide

  24. Transitive dependency
    management

    View full-size slide

  25. Stay up to date!

    View full-size slide

  26. Hermiticity is hard.

    View full-size slide

  27. iOS is second class

    View full-size slide


  28. Bazel iOS

    View full-size slide

  29. IDE Integration

    View full-size slide

  30. Xcode Bazel Integration
    ● Tulsi – Google
    ● XCHammer – Pinterest

    View full-size slide

  31. Like Google and
    Pinterest, we made
    our own

    View full-size slide

  32. Generating Projects

    View full-size slide

  33. Generating Projects
    gen_project(b: Bazel) -> XcodeProject

    View full-size slide

  34. Generating Projects
    gen_project(b: Bazel) -> XcodeProject

    View full-size slide

  35. Exporting Bazel Projects
    ● bazel build --nobuild --aspects
    ● bazel query --output=xml
    ● bazel cquery --output=proto

    View full-size slide

  36. Exporting Bazel Projects
    ● bazel build --nobuild --aspects
    ● bazel query --output=xml
    ● bazel cquery --output=proto

    View full-size slide

  37. Generating Projects
    gen_project(Bazel) -> XcodeProject

    View full-size slide

  38. Generating Projects
    XcodeProject[XcodeBuild]
    XcodeProject[BazelBuild]

    View full-size slide

  39. Generating Projects
    XcodeProject[XcodeBuild]
    XcodeProject[BazelBuild]

    View full-size slide

  40. Incremental Migration
    1A. Convert to Bazel as the build source of truth and build on CI
    1B. Generate Xcode projects from Bazel, and build with Xcode

    View full-size slide

  41. Generating Projects
    XcodeProject[XcodeBuild]
    XcodeProject[BazelBuild]

    View full-size slide

  42. This is two operations:
    add Bazel, also
    remove xcodebuild

    View full-size slide

  43. Integrating bazel

    View full-size slide

  44. Integrating bazel in Xcode
    ● "External Build System" target

    View full-size slide

  45. Integrating bazel in Xcode
    ● "External Build System" target
    ● "Run Script" phase

    View full-size slide

  46. Running & Testing

    View full-size slide

  47. Running & Testing
    ● Copy .app or .xctest to Xcode DerivedData (ie "bazel-out")
    ● Xcode does the rest, which helps preserves many IDE features

    View full-size slide

  48. More to Integrating bazel in Xcode
    ● Installing apps and tests
    ● Appease the Xcode build, by copying build artifacts
    ● Supporting the IDE by importing build outputs
    ● Generating debugger settings

    View full-size slide

  49. Basically, an ad-hoc,
    informally specified,
    installer

    View full-size slide

  50. Hyrum's Law
    "With a sufficient number of users of an API,
    it does not matter what you promise in the contract:
    all observable behaviors of your system
    will be depended on by somebody."
    - Hyrum Wright, Googler

    View full-size slide

  51. Decoupling xcodebuild

    View full-size slide

  52. Decoupling xcodebuild
    ● CC=/usr/bin/true
    ● LD=/usr/bin/true
    ● LIBTOOL=/usr/bin/true
    ● SWIFT_EXEC=/usr/bin/true

    View full-size slide

  53. Decoupling xcodebuild
    ● CC=/usr/bin/true
    ● LD=/usr/bin/true
    ● LIBTOOL=/usr/bin/true
    ● SWIFT_EXEC=./tools/swiftc-stub.sh

    View full-size slide

  54. Indexing Facts of Life
    ● Backbone of important IDE features
    ● Common developer slowdown
    ● Usually proprietary
    ● Out of band

    View full-size slide

  55. Indexing Integration
    ● swiftc & clang can produce indexes while building
    ● Indexes embed some absolute paths
    ● swiftc & clang and their component libraries are open source
    ● index-import: Purpose built tool for installing indexes into Xcode
    ● https://github.com/lyft/index-import

    View full-size slide

  56. Debugging, with lldb
    ● Paths have been at the core of every challenge
    ● Source paths, binary paths, absolute paths, relative paths, project paths, build
    paths
    ● Fix by removing paths, or making them relative

    View full-size slide

  57. Debugging, with lldb
    ● Compiler flags, with the help of Bazel wrapped clang, rules_swift worker
    ● Linking locally to ensure the binary has local paths
    ● Empty swift module, locally compiled and bearing embedded debugging options
    ● Generating lldb settings and wiring them into developer's global settings

    View full-size slide

  58. Finally, everything
    works

    View full-size slide

  59. Stepping Back

    View full-size slide

  60. Stepping Back
    1. Build with Bazel
    2. Customizable project generation
    3. Taken the build away from Xcode

    View full-size slide

  61. Focused Subprojects

    View full-size slide

  62. Pick any targets,
    dependencies not
    required

    View full-size slide

  63. Focused Subprojects
    ● Generating projects is faster
    ● Launching Xcode and time to usability is faster
    ● Provide natural scopes for teams/projects
    ● Performance scales by developer scope, not repo

    View full-size slide

  64. IDE Finer Details

    View full-size slide

  65. IDE Finer Details
    ● Native Xcode test experience
    ● Augmented project search
    ● Compiler fixits
    ● Full index, not focused index
    ● Refactoring
    ● Remote cache!

    View full-size slide

  66. Unresolved
    ● Progress bar is useless, build log looks different
    ● No background indexing
    ● bazel×swiftc parallelism mismatch
    ● Code coverage features don't work

    View full-size slide

  67. Questions!
    @SmileyKeith, @kastiglione

    View full-size slide