Skip to content

Commit

Permalink
perf: allow concurrent flutter pub get
Browse files Browse the repository at this point in the history
  • Loading branch information
soumyamahunt committed May 3, 2022
1 parent 0887247 commit ef5bc8a
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 35 deletions.
5 changes: 1 addition & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ source 'https://rubygems.org'
gemspec

group :development do
gem 'cocoapods'
gem 'cocoapods-plugins'
gem 'github_api'

gem 'mocha'
gem 'bacon'
gem 'mocha-on-bacon'
gem 'prettybacon'
gem 'solargraph'
gem 'github_api'
end
3 changes: 1 addition & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ PATH
specs:
cocoapods-embed-flutter (0.6.0)
cocoapods
concurrent-ruby
fileutils
yaml

Expand Down Expand Up @@ -214,9 +215,7 @@ PLATFORMS
DEPENDENCIES
bacon
bundler
cocoapods
cocoapods-embed-flutter!
cocoapods-plugins
github_api
mocha
mocha-on-bacon
Expand Down
1 change: 1 addition & 0 deletions cocoapods-embed-flutter.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'yaml'
spec.add_runtime_dependency 'fileutils'
spec.add_runtime_dependency 'cocoapods'
spec.add_runtime_dependency 'concurrent-ruby'

spec.add_development_dependency 'bundler'
spec.add_development_dependency 'rake'
Expand Down
1 change: 1 addition & 0 deletions example/ios_app/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ PATH
specs:
cocoapods-embed-flutter (0.6.0)
cocoapods
concurrent-ruby
fileutils
yaml

Expand Down
29 changes: 22 additions & 7 deletions lib/cocoapods-embed-flutter/flutter/dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ class Dependency
# @param [String, Hash] requirements
# the requirements for dependency as declred in `pubspec`
#
# @param [Spec] parent_specification
# @param [Spec] parent_spec
# the parent specification where dependency declared
#
# @param [Boolean] is_dev_dependency
# @param [Boolean] dev_dependency
# Whether the dependency only required during development
#
def initialize(name, requirements, parent_spec, dev_dependency = false)
Expand All @@ -48,10 +48,10 @@ def initialize(name, requirements, parent_spec, dev_dependency = false)
# @param [Hash] hash declared in `dependencies` or `dev_dependencies`
# section in `pubspec.yaml` file
#
# @param [Spec] parent_specification
# @param [Spec] parent_spec
# the parent specification where dependency declared
#
# @param [Boolean] is_dev_dependency
# @param [Boolean] dev_dependency
# Whether the dependency only required during development
#
# @return [Array<Dependency>] dependencies from hash declared in `dependencies`
Expand All @@ -74,14 +74,29 @@ def spec
Spec.find(name, File.expand_path(path, File.dirname(parent_spec.defined_in_file)))
end

# Install this dependency for the parent project.
# Concurrently install this dependency for the parent project.
#
# @return [void]
# @return [Concurrent::Promises::Future, Nil]
# {Nil} if not a local dependency, otherwise
# returns future for {#spec}'s {Spec#pub_get pub_get} task.
#
def install
spec.setup if local?
spec.pub_get if local?
end

# Allows accessing top level values in
# {https://dart.dev/tools/pub/dependencies dependency requirements},
# if {#requirements} type is {Hash}, i.e. path, git etc.
#
# @param [Symbol] m
# top level key value to access, i.e. path, git etc.
#
# @return depending on accessed value type in {#requirements}.
#
# @raise [NoMethodError] if no method or custom attribute exists by
# the attribute name in {#requirements} or {#requirements}
# is not a {Hash}.
#
def method_missing(m, *args, &block)
if requirements.is_a?(Hash) && requirements.include?(m.to_s)
return requirements[m.to_s]
Expand Down
3 changes: 3 additions & 0 deletions lib/cocoapods-embed-flutter/flutter/downloader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

module Flutter
module Pub
# The Downloader modules name-spaces all the classes and methods
# for downloading and caching remote Flutter projects.
#
module Downloader
# Downloads a package from the given `request` to the given `target` location.
#
Expand Down
6 changes: 3 additions & 3 deletions lib/cocoapods-embed-flutter/flutter/external_sources.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def self.fetchWithNameAndOptions(name, options)
if SOURCE_KEYS.keys.any? { |key| options.key?(key) }
source = DownloaderSource.new(name, options, Pod::Config.instance.podfile_path)
source.fetch(Pod::Config.instance.sandbox)
path = source.normalized_pupspec_path
path = source.normalized_pubspec_path
elsif options.key?(:path)
path = options[:path]
else
Expand Down Expand Up @@ -137,7 +137,7 @@ def description
# @note If the declared path is expanded only if the represents a path
# relative to the file system.
#
def normalized_pupspec_path(declared_path)
def normalized_pubspec_path(declared_path)
Spec.find_file(name, declared_path)
end

Expand All @@ -147,7 +147,7 @@ def normalized_pupspec_path(declared_path)
# @return [String] The uri of the pubspec appending the name of the file
# and expanding it if necessary.
#
def normalized_pupspec_path
def normalized_pubspec_path
search_path = params[:path].nil? ? target : File.expand_path(params[:path], target)
Spec.find_file(name, search_path)
end
Expand Down
69 changes: 51 additions & 18 deletions lib/cocoapods-embed-flutter/flutter/pubspec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
require 'cocoapods-embed-flutter/flutter'
require 'yaml'
require 'open3'
require 'concurrent'
require 'cocoapods'

module Flutter
module Pub
Expand Down Expand Up @@ -43,7 +46,8 @@ def self.find_file(name, path)

if File.basename(path) == Pub::SPEC_FILE
return path
elsif Dir.exists?(File.expand_path(name, path)) && File.exists?(File.expand_path(Pub::SPEC_FILE, File.expand_path(name, path)))
elsif Dir.exists?(File.expand_path(name, path)) &&
File.exists?(File.expand_path(Pub::SPEC_FILE, File.expand_path(name, path)))
return File.expand_path(Pub::SPEC_FILE, File.expand_path(name, path))
elsif File.exists?(File.expand_path(Pub::SPEC_FILE, path))
return File.expand_path(Pub::SPEC_FILE, path)
Expand Down Expand Up @@ -88,7 +92,7 @@ def project_path
end

# @return [String] the path to the flutter project
# dependencies cache file.
# dependencies cache file.
#
def package_cache_path
File.join(project_path, Pub::TOOL_DIR, Pub::CACHE_FILE)
Expand All @@ -101,7 +105,7 @@ def pod_helper_path
end

# @return [Array<Dependency>] the list of all the projects this
# specification depends upon and are included in app release.
# specification depends upon and are included in app release.
#
def dependencies
return [] unless @data.include?('dependencies')
Expand All @@ -123,37 +127,66 @@ def all_dependencies
dependencies + dev_dependencies
end

# @return [Boolean] If the flutter project for this specification
# has all its dependencies installed.
# Runs `flutter pub get` on project directory concurrently.
#
def setup?
File.exists?(package_cache_path) && (!module? || File.exists?(pod_helper_path))
# @return [Concurrent::Promises::Future, Nil]
# {Nil} if `pub get` running/completed, otherwise
# runs `flutter pub get` task in background
# and returns its future.
#
def pub_get
future = @@current_pubgets[self]
return nil if !future.nil?
future = Concurrent::Promises.future do
stdout, stderr, status = Open3.capture3('flutter pub get', :chdir => self.project_path)
:result
end
@@current_pubgets[self] = future
return Concurrent::Promises.zip(future, *all_dependencies.map(&:install).compact)
end

# Sets up the project installing all specified dependencies.
# See if two {Spec} instances refer to the same pubspecs.
#
# @return [void]
# @return [Boolean] whether or not the two {Spec} instances refer to the
# same projects.
#
def setup
return if setup?
pup_get
all_dependencies.each(&:install)
def ==(other)
self.class === other &&
other.defined_in_file == defined_in_file &&
other.instance_variable_get(:@data) == @data
end

# Runs `flutter pub get` on project directory.
#
# @return [void]
# @return [Fixnum] A hash identical for equals objects.
#
def pup_get
Dir.chdir(project_path) { |path| system('flutter pub get', exception: true) }
def hash
[defined_in_file, @data].hash
end

alias eql? ==

# Allows accessing top level values in `pubspec.yaml`,
# i.e. name, description, version etc.
#
# @param [Symbol] m
# top level key value to access,
# i.e. name, description etc.
#
# @return depending on accessed value type in `pubspec.yaml`.
#
# @raise [NoMethodError] if no method or custom attribute exists by
# the attribute name in pubspec.
#
def method_missing(m, *args, &block)
if @data.include?(m.to_s)
return @data[m.to_s]
end
super.method_missing(m, *args, &block)
end

private

# A hash containing all `pub get` promises.
@@current_pubgets = {}
end
end
end
5 changes: 4 additions & 1 deletion lib/cocoapods-embed-flutter/src/pub.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ module DSL
#
def pub(name = nil, *requirements)
pubspec = Flutter::Pub::ExternalSources.fetchWithNameAndOptions(name, requirements)
pubspec.setup
Pod::UI.titled_section("Installing flutter dependencies for #{name}...", :verbose_prefix => '-> ') do
future = pubspec.pub_get
future.value! if !future.nil?
end
raise StandardError, "Invalid flutter module: '#{name}'." unless File.exists?(pubspec.pod_helper_path)
install_flutter_pods_for_pubspec(pubspec)
end
Expand Down

0 comments on commit ef5bc8a

Please sign in to comment.