From 535e556aa83a0bb582b24e0905a2dfedefb24646 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 29 Jun 2020 00:27:28 +0200 Subject: [PATCH] Refactor PathScanner to avoid Dir.glob --- lib/bootsnap/load_path_cache/path_scanner.rb | 65 ++++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/lib/bootsnap/load_path_cache/path_scanner.rb b/lib/bootsnap/load_path_cache/path_scanner.rb index 10992dc5..ee278419 100644 --- a/lib/bootsnap/load_path_cache/path_scanner.rb +++ b/lib/bootsnap/load_path_cache/path_scanner.rb @@ -5,7 +5,6 @@ module Bootsnap module LoadPathCache module PathScanner - ALL_FILES = "/{,**/*/**/}*" REQUIRABLE_EXTENSIONS = [DOT_RB] + DL_EXTENSIONS NORMALIZE_NATIVE_EXTENSIONS = !DL_EXTENSIONS.include?(LoadPathCache::DOT_SO) ALTERNATIVE_NATIVE_EXTENSIONS_PATTERN = /\.(o|bundle|dylib)\z/ @@ -16,35 +15,47 @@ module PathScanner '' end - def self.call(path) - path = path.to_s - - relative_slice = (path.size + 1)..-1 - # If the bundle path is a descendent of this path, we do additional - # checks to prevent recursing into the bundle path as we recurse - # through this path. We don't want to scan the bundle path because - # anything useful in it will be present on other load path items. - # - # This can happen if, for example, the user adds '.' to the load path, - # and the bundle path is '.bundle'. - contains_bundle_path = BUNDLE_PATH.start_with?(path) - - dirs = [] - requirables = [] - - Dir.glob(path + ALL_FILES).each do |absolute_path| - absolute_path.freeze - next if contains_bundle_path && absolute_path.start_with?(BUNDLE_PATH) - relative_path = absolute_path.slice(relative_slice).freeze - - if File.directory?(absolute_path) - dirs << relative_path - elsif REQUIRABLE_EXTENSIONS.include?(File.extname(relative_path)) - requirables << relative_path + class << self + def call(path) + path = File.expand_path(path.to_s).freeze + + # If the bundle path is a descendent of this path, we do additional + # checks to prevent recursing into the bundle path as we recurse + # through this path. We don't want to scan the bundle path because + # anything useful in it will be present on other load path items. + # + # This can happen if, for example, the user adds '.' to the load path, + # and the bundle path is '.bundle'. + contains_bundle_path = BUNDLE_PATH.start_with?(path) + + dirs = [] + requirables = [] + walk(path, nil) do |relative_path, absolute_path, is_directory| + if is_directory + dirs << relative_path + !contains_bundle_path || !absolute_path.start_with?(BUNDLE_PATH) + elsif relative_path.end_with?(*REQUIRABLE_EXTENSIONS) + requirables << relative_path + end end + [requirables, dirs] end - [requirables, dirs] + def walk(absolute_dir_path, relative_dir_path, &block) + Dir.foreach(absolute_dir_path) do |name| + next if name.start_with?('.') + relative_path = relative_dir_path ? "#{relative_dir_path}/#{name}" : name.freeze + + absolute_path = "#{absolute_dir_path}/#{name}" + if File.directory?(absolute_path) + if yield relative_path, absolute_path, true + walk(absolute_path, relative_path, &block) + end + else + yield relative_path, absolute_path, false + end + end + end end end end