Skip to content

Commit ef5bc8a

Browse files
committed
perf: allow concurrent flutter pub get
1 parent 0887247 commit ef5bc8a

File tree

9 files changed

+87
-35
lines changed

9 files changed

+87
-35
lines changed

Gemfile

+1-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ source 'https://rubygems.org'
44
gemspec
55

66
group :development do
7-
gem 'cocoapods'
8-
gem 'cocoapods-plugins'
9-
gem 'github_api'
10-
117
gem 'mocha'
128
gem 'bacon'
139
gem 'mocha-on-bacon'
1410
gem 'prettybacon'
1511
gem 'solargraph'
12+
gem 'github_api'
1613
end

Gemfile.lock

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PATH
33
specs:
44
cocoapods-embed-flutter (0.6.0)
55
cocoapods
6+
concurrent-ruby
67
fileutils
78
yaml
89

@@ -214,9 +215,7 @@ PLATFORMS
214215
DEPENDENCIES
215216
bacon
216217
bundler
217-
cocoapods
218218
cocoapods-embed-flutter!
219-
cocoapods-plugins
220219
github_api
221220
mocha
222221
mocha-on-bacon

cocoapods-embed-flutter.gemspec

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
2727
spec.add_runtime_dependency 'yaml'
2828
spec.add_runtime_dependency 'fileutils'
2929
spec.add_runtime_dependency 'cocoapods'
30+
spec.add_runtime_dependency 'concurrent-ruby'
3031

3132
spec.add_development_dependency 'bundler'
3233
spec.add_development_dependency 'rake'

example/ios_app/Gemfile.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PATH
33
specs:
44
cocoapods-embed-flutter (0.6.0)
55
cocoapods
6+
concurrent-ruby
67
fileutils
78
yaml
89

lib/cocoapods-embed-flutter/flutter/dependency.rb

+22-7
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ class Dependency
2727
# @param [String, Hash] requirements
2828
# the requirements for dependency as declred in `pubspec`
2929
#
30-
# @param [Spec] parent_specification
30+
# @param [Spec] parent_spec
3131
# the parent specification where dependency declared
3232
#
33-
# @param [Boolean] is_dev_dependency
33+
# @param [Boolean] dev_dependency
3434
# Whether the dependency only required during development
3535
#
3636
def initialize(name, requirements, parent_spec, dev_dependency = false)
@@ -48,10 +48,10 @@ def initialize(name, requirements, parent_spec, dev_dependency = false)
4848
# @param [Hash] hash declared in `dependencies` or `dev_dependencies`
4949
# section in `pubspec.yaml` file
5050
#
51-
# @param [Spec] parent_specification
51+
# @param [Spec] parent_spec
5252
# the parent specification where dependency declared
5353
#
54-
# @param [Boolean] is_dev_dependency
54+
# @param [Boolean] dev_dependency
5555
# Whether the dependency only required during development
5656
#
5757
# @return [Array<Dependency>] dependencies from hash declared in `dependencies`
@@ -74,14 +74,29 @@ def spec
7474
Spec.find(name, File.expand_path(path, File.dirname(parent_spec.defined_in_file)))
7575
end
7676

77-
# Install this dependency for the parent project.
77+
# Concurrently install this dependency for the parent project.
7878
#
79-
# @return [void]
79+
# @return [Concurrent::Promises::Future, Nil]
80+
# {Nil} if not a local dependency, otherwise
81+
# returns future for {#spec}'s {Spec#pub_get pub_get} task.
8082
#
8183
def install
82-
spec.setup if local?
84+
spec.pub_get if local?
8385
end
8486

87+
# Allows accessing top level values in
88+
# {https://dart.dev/tools/pub/dependencies dependency requirements},
89+
# if {#requirements} type is {Hash}, i.e. path, git etc.
90+
#
91+
# @param [Symbol] m
92+
# top level key value to access, i.e. path, git etc.
93+
#
94+
# @return depending on accessed value type in {#requirements}.
95+
#
96+
# @raise [NoMethodError] if no method or custom attribute exists by
97+
# the attribute name in {#requirements} or {#requirements}
98+
# is not a {Hash}.
99+
#
85100
def method_missing(m, *args, &block)
86101
if requirements.is_a?(Hash) && requirements.include?(m.to_s)
87102
return requirements[m.to_s]

lib/cocoapods-embed-flutter/flutter/downloader.rb

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
module Flutter
55
module Pub
6+
# The Downloader modules name-spaces all the classes and methods
7+
# for downloading and caching remote Flutter projects.
8+
#
69
module Downloader
710
# Downloads a package from the given `request` to the given `target` location.
811
#

lib/cocoapods-embed-flutter/flutter/external_sources.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def self.fetchWithNameAndOptions(name, options)
4141
if SOURCE_KEYS.keys.any? { |key| options.key?(key) }
4242
source = DownloaderSource.new(name, options, Pod::Config.instance.podfile_path)
4343
source.fetch(Pod::Config.instance.sandbox)
44-
path = source.normalized_pupspec_path
44+
path = source.normalized_pubspec_path
4545
elsif options.key?(:path)
4646
path = options[:path]
4747
else
@@ -137,7 +137,7 @@ def description
137137
# @note If the declared path is expanded only if the represents a path
138138
# relative to the file system.
139139
#
140-
def normalized_pupspec_path(declared_path)
140+
def normalized_pubspec_path(declared_path)
141141
Spec.find_file(name, declared_path)
142142
end
143143

@@ -147,7 +147,7 @@ def normalized_pupspec_path(declared_path)
147147
# @return [String] The uri of the pubspec appending the name of the file
148148
# and expanding it if necessary.
149149
#
150-
def normalized_pupspec_path
150+
def normalized_pubspec_path
151151
search_path = params[:path].nil? ? target : File.expand_path(params[:path], target)
152152
Spec.find_file(name, search_path)
153153
end

lib/cocoapods-embed-flutter/flutter/pubspec.rb

+51-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
require 'cocoapods-embed-flutter/flutter'
22
require 'yaml'
3+
require 'open3'
4+
require 'concurrent'
5+
require 'cocoapods'
36

47
module Flutter
58
module Pub
@@ -43,7 +46,8 @@ def self.find_file(name, path)
4346

4447
if File.basename(path) == Pub::SPEC_FILE
4548
return path
46-
elsif Dir.exists?(File.expand_path(name, path)) && File.exists?(File.expand_path(Pub::SPEC_FILE, File.expand_path(name, path)))
49+
elsif Dir.exists?(File.expand_path(name, path)) &&
50+
File.exists?(File.expand_path(Pub::SPEC_FILE, File.expand_path(name, path)))
4751
return File.expand_path(Pub::SPEC_FILE, File.expand_path(name, path))
4852
elsif File.exists?(File.expand_path(Pub::SPEC_FILE, path))
4953
return File.expand_path(Pub::SPEC_FILE, path)
@@ -88,7 +92,7 @@ def project_path
8892
end
8993

9094
# @return [String] the path to the flutter project
91-
# dependencies cache file.
95+
# dependencies cache file.
9296
#
9397
def package_cache_path
9498
File.join(project_path, Pub::TOOL_DIR, Pub::CACHE_FILE)
@@ -101,7 +105,7 @@ def pod_helper_path
101105
end
102106

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

126-
# @return [Boolean] If the flutter project for this specification
127-
# has all its dependencies installed.
130+
# Runs `flutter pub get` on project directory concurrently.
128131
#
129-
def setup?
130-
File.exists?(package_cache_path) && (!module? || File.exists?(pod_helper_path))
132+
# @return [Concurrent::Promises::Future, Nil]
133+
# {Nil} if `pub get` running/completed, otherwise
134+
# runs `flutter pub get` task in background
135+
# and returns its future.
136+
#
137+
def pub_get
138+
future = @@current_pubgets[self]
139+
return nil if !future.nil?
140+
future = Concurrent::Promises.future do
141+
stdout, stderr, status = Open3.capture3('flutter pub get', :chdir => self.project_path)
142+
:result
143+
end
144+
@@current_pubgets[self] = future
145+
return Concurrent::Promises.zip(future, *all_dependencies.map(&:install).compact)
131146
end
132147

133-
# Sets up the project installing all specified dependencies.
148+
# See if two {Spec} instances refer to the same pubspecs.
134149
#
135-
# @return [void]
150+
# @return [Boolean] whether or not the two {Spec} instances refer to the
151+
# same projects.
136152
#
137-
def setup
138-
return if setup?
139-
pup_get
140-
all_dependencies.each(&:install)
153+
def ==(other)
154+
self.class === other &&
155+
other.defined_in_file == defined_in_file &&
156+
other.instance_variable_get(:@data) == @data
141157
end
142158

143-
# Runs `flutter pub get` on project directory.
144-
#
145-
# @return [void]
159+
# @return [Fixnum] A hash identical for equals objects.
146160
#
147-
def pup_get
148-
Dir.chdir(project_path) { |path| system('flutter pub get', exception: true) }
161+
def hash
162+
[defined_in_file, @data].hash
149163
end
150164

165+
alias eql? ==
166+
167+
# Allows accessing top level values in `pubspec.yaml`,
168+
# i.e. name, description, version etc.
169+
#
170+
# @param [Symbol] m
171+
# top level key value to access,
172+
# i.e. name, description etc.
173+
#
174+
# @return depending on accessed value type in `pubspec.yaml`.
175+
#
176+
# @raise [NoMethodError] if no method or custom attribute exists by
177+
# the attribute name in pubspec.
178+
#
151179
def method_missing(m, *args, &block)
152180
if @data.include?(m.to_s)
153181
return @data[m.to_s]
154182
end
155183
super.method_missing(m, *args, &block)
156184
end
185+
186+
private
187+
188+
# A hash containing all `pub get` promises.
189+
@@current_pubgets = {}
157190
end
158191
end
159192
end

lib/cocoapods-embed-flutter/src/pub.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ module DSL
112112
#
113113
def pub(name = nil, *requirements)
114114
pubspec = Flutter::Pub::ExternalSources.fetchWithNameAndOptions(name, requirements)
115-
pubspec.setup
115+
Pod::UI.titled_section("Installing flutter dependencies for #{name}...", :verbose_prefix => '-> ') do
116+
future = pubspec.pub_get
117+
future.value! if !future.nil?
118+
end
116119
raise StandardError, "Invalid flutter module: '#{name}'." unless File.exists?(pubspec.pod_helper_path)
117120
install_flutter_pods_for_pubspec(pubspec)
118121
end

0 commit comments

Comments
 (0)