@@ -10,8 +10,8 @@ use ruff_linter::{warn_user, warn_user_once};
10
10
use ruff_python_ast:: { PySourceType , SourceType } ;
11
11
use ruff_workspace:: resolver:: { python_files_in_path, ResolvedFile } ;
12
12
use rustc_hash:: FxHashMap ;
13
- use std:: path:: Path ;
14
- use std:: sync:: Arc ;
13
+ use std:: path:: { Path , PathBuf } ;
14
+ use std:: sync:: { Arc , Mutex } ;
15
15
16
16
/// Generate an import map.
17
17
pub ( crate ) fn analyze_graph (
@@ -51,7 +51,7 @@ pub(crate) fn analyze_graph(
51
51
. map ( |( path, package) | ( path. to_path_buf ( ) , package. map ( Path :: to_path_buf) ) )
52
52
. collect :: < FxHashMap < _ , _ > > ( ) ;
53
53
54
- // Create a database for each source root .
54
+ // Create a database from the source roots .
55
55
let db = ModuleDb :: from_src_roots (
56
56
package_roots
57
57
. values ( )
@@ -61,8 +61,11 @@ pub(crate) fn analyze_graph(
61
61
. filter_map ( |path| SystemPathBuf :: from_path_buf ( path) . ok ( ) ) ,
62
62
) ?;
63
63
64
+ // Create a cache for resolved globs.
65
+ let glob_resolver = Arc :: new ( Mutex :: new ( GlobResolver :: default ( ) ) ) ;
66
+
64
67
// Collect and resolve the imports for each file.
65
- let result = Arc :: new ( std :: sync :: Mutex :: new ( Vec :: new ( ) ) ) ;
68
+ let result = Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ;
66
69
let inner_result = Arc :: clone ( & result) ;
67
70
68
71
rayon:: scope ( move |scope| {
@@ -111,6 +114,7 @@ pub(crate) fn analyze_graph(
111
114
let db = db. snapshot ( ) ;
112
115
let root = root. clone ( ) ;
113
116
let result = inner_result. clone ( ) ;
117
+ let glob_resolver = glob_resolver. clone ( ) ;
114
118
scope. spawn ( move |_| {
115
119
// Identify any imports via static analysis.
116
120
let mut imports =
@@ -120,38 +124,12 @@ pub(crate) fn analyze_graph(
120
124
ModuleImports :: default ( )
121
125
} ) ;
122
126
127
+ debug ! ( "Discovered {} imports for {}" , imports. len( ) , path) ;
128
+
123
129
// Append any imports that were statically defined in the configuration.
124
130
if let Some ( ( root, globs) ) = include_dependencies {
125
- match globwalk:: GlobWalkerBuilder :: from_patterns ( root, & globs)
126
- . file_type ( globwalk:: FileType :: FILE )
127
- . build ( )
128
- {
129
- Ok ( walker) => {
130
- for entry in walker {
131
- let entry = match entry {
132
- Ok ( entry) => entry,
133
- Err ( err) => {
134
- warn ! ( "Failed to read glob entry: {err}" ) ;
135
- continue ;
136
- }
137
- } ;
138
- let path = match SystemPathBuf :: from_path_buf ( entry. into_path ( ) ) {
139
- Ok ( path) => path,
140
- Err ( err) => {
141
- warn ! (
142
- "Failed to convert path to system path: {}" ,
143
- err. display( )
144
- ) ;
145
- continue ;
146
- }
147
- } ;
148
- imports. insert ( path) ;
149
- }
150
- }
151
- Err ( err) => {
152
- warn ! ( "Failed to read glob walker: {err}" ) ;
153
- }
154
- }
131
+ let mut glob_resolver = glob_resolver. lock ( ) . unwrap ( ) ;
132
+ imports. extend ( glob_resolver. resolve ( root, globs) ) ;
155
133
}
156
134
157
135
// Convert the path (and imports) to be relative to the working directory.
@@ -180,3 +158,67 @@ pub(crate) fn analyze_graph(
180
158
181
159
Ok ( ExitStatus :: Success )
182
160
}
161
+
162
+ /// A resolver for glob sets.
163
+ #[ derive( Default , Debug ) ]
164
+ struct GlobResolver {
165
+ cache : GlobCache ,
166
+ }
167
+
168
+ impl GlobResolver {
169
+ /// Resolve a set of globs, anchored at a given root.
170
+ fn resolve ( & mut self , root : PathBuf , globs : Vec < String > ) -> Vec < SystemPathBuf > {
171
+ if let Some ( cached) = self . cache . get ( & root, & globs) {
172
+ return cached. clone ( ) ;
173
+ }
174
+
175
+ let walker = match globwalk:: GlobWalkerBuilder :: from_patterns ( & root, & globs)
176
+ . file_type ( globwalk:: FileType :: FILE )
177
+ . build ( )
178
+ {
179
+ Ok ( walker) => walker,
180
+ Err ( err) => {
181
+ warn ! ( "Failed to read glob walker: {err}" ) ;
182
+ return Vec :: new ( ) ;
183
+ }
184
+ } ;
185
+
186
+ let mut paths = Vec :: new ( ) ;
187
+ for entry in walker {
188
+ let entry = match entry {
189
+ Ok ( entry) => entry,
190
+ Err ( err) => {
191
+ warn ! ( "Failed to read glob entry: {err}" ) ;
192
+ continue ;
193
+ }
194
+ } ;
195
+ let path = match SystemPathBuf :: from_path_buf ( entry. into_path ( ) ) {
196
+ Ok ( path) => path,
197
+ Err ( err) => {
198
+ warn ! ( "Failed to convert path to system path: {}" , err. display( ) ) ;
199
+ continue ;
200
+ }
201
+ } ;
202
+ paths. push ( path) ;
203
+ }
204
+
205
+ self . cache . insert ( root, globs, paths. clone ( ) ) ;
206
+ paths
207
+ }
208
+ }
209
+
210
+ /// A cache for resolved globs.
211
+ #[ derive( Default , Debug ) ]
212
+ struct GlobCache ( FxHashMap < PathBuf , FxHashMap < Vec < String > , Vec < SystemPathBuf > > > ) ;
213
+
214
+ impl GlobCache {
215
+ /// Insert a resolved glob.
216
+ fn insert ( & mut self , root : PathBuf , globs : Vec < String > , paths : Vec < SystemPathBuf > ) {
217
+ self . 0 . entry ( root) . or_default ( ) . insert ( globs, paths) ;
218
+ }
219
+
220
+ /// Get a resolved glob.
221
+ fn get ( & self , root : & Path , globs : & [ String ] ) -> Option < & Vec < SystemPathBuf > > {
222
+ self . 0 . get ( root) . and_then ( |map| map. get ( globs) )
223
+ }
224
+ }
0 commit comments