Files
2025-09-29 00:52:08 +02:00

199 lines
5.7 KiB
Ruby
Executable File

#
# File:: data_string_analyser.rb
# Description::
#
# Author:: David Muir <david.muir@rockstarnorth.com>
# Date:: 11 November 2008
#
#----------------------------------------------------------------------------
# Uses
#----------------------------------------------------------------------------
require 'pipeline/os/path'
require 'pipeline/util/string'
#----------------------------------------------------------------------------
# Implementation
#----------------------------------------------------------------------------
module Pipeline
module ProjectUtil
#
# == Description
# The main code dependency analyser entry-point. This function takes a
# list of files to analyse using the registered StringAnalyser classes
# and invokes the user-block for each dependency it finds.
#
# The user-block should take two arguments, the first being the absolute
# path of the file being analysed, the second being the string recognised
# by the analyser as the dependency.
#
# The dependencies are also returned in a Hash with filenames as the
# key and an Array of dependency String objects.
#
# === Example Usage
#
# filelist = OS::find_files_recurse( root_path, '*.*' )
# ProjectUtil::code_dependency_analyser( filelist ) do |file, dependency|
# ## User-code
# end
#
def ProjectUtil::code_dependency_analyser( files, analyser, &block )
throw ArgumentError.new( "Invalid StringAnalyser specified (#{analyser.class})." ) \
unless ( analyser.is_a?( StringAnalyser ) )
dependencies = {}
files.each do |filename|
# Skip file if our analyser doesn't know about it.
extension = OS::Path::get_extension( filename )
next unless ( analyser.extensions.include?( extension ) )
# Read all file data as text.
data = []
File.open( filename, 'r' ) do |fp|
data = fp.readlines( )
end
# Loop through file content line by line.
data.each do |line|
# Check each pattern.
analyser.patterns.each_pair do |regexp, index|
md = line.match( regexp )
next if ( md.nil? )
# We have a match so we invoke the user block.
yield( filename, md[index] ) if ( block_given? )
# Complete Hash being returned.
dependencies[filename] = [] unless ( dependencies.has_key?( filename ) )
dependencies[filename] << md[index]
end
end
end
dependencies
end
#
# == Description
# String analyser base class. The constructor takes a file extension
# argument and a Hash object mapping regular expressions to integer
# indices within the matchdata Array for the (typically filename)
# dependency.
#
# In general, the index Hash value will be 1 unless you are reusing the
# Regexp object from another script.
#
class StringAnalyser
attr_reader :extensions
attr_reader :patterns
def initialize( extensions, patterns )
throw ArgumentError.new( "Invalid Array of extensions (#{extensions.class})." ) \
unless ( extensions.is_a?( Array ) )
throw ArgumentError.new( "Invalid Hash of patterns (#{patterns.class})." ) \
unless ( patterns.is_a?( Hash ) )
@extensions = extensions
@patterns = patterns
end
def to_s()
"#{self.class}: #{@extensions.join( ' ' )} #{@patterns.join( '; ' )}"
end
def StringAnalyser::registered_analysers( )
@@registered_analysers
end
def StringAnalyser::inherited( subclass )
@@registered_analysers = [] if ( @registered_analysers.nil? )
@@registered_analysers << subclass
end
end
#
# == Description
# Ruby file string dependency analyser.
#
class RubyFileDependencyAnalyser < StringAnalyser
def initialize( )
super( EXTENSIONS, PATTERNS )
end
#----------------------------------------------------------------------
# Private Data
#----------------------------------------------------------------------
private
EXTENSIONS = [ 'rb', 'rbw' ]
# Define some useful Regexp objects to fill in our patterns Hash.
REGEXP_REQUIRE_DQ = /^require \"(.*)\"/i
REGEXP_REQUIRE_SQ = /^require \'(.*)\'/i
REGEXP_GEM_DQ = /^gem \"(.*)\"/i
REGEXP_GEM_SQ = /^gem \'(.*)\'/i
# Define our patterns hash, using our regexp constants above.
PATTERNS = {
REGEXP_REQUIRE_DQ => 1,
REGEXP_REQUIRE_SQ => 1,
REGEXP_GEM_DQ => 1,
REGEXP_GEM_SQ => 1,
}
end
#
# == Description
# MaxScript file string dependency analyser.
#
class MaxScriptFileDependencyAnalyser < StringAnalyser
def initialize( )
super( EXTENSIONS, PATTERNS )
end
#----------------------------------------------------------------------
# Private Data
#----------------------------------------------------------------------
private
EXTENSIONS = [ 'ms', 'mcr' ]
# Define some useful Regexp objects to fill in our patterns Hash.
REGEXP_FILEIN = /^filein \"(.*)\"/i
REGEXP_INCLUDE = /^include \"(.*)\"/i
# Define our patterns hash, using our regexp constants above.
PATTERNS = {
REGEXP_FILEIN => 1,
REGEXP_INCLUDE => 1,
}
end
end # ProjectUtil module
end # Pipeline module
if ( __FILE__ == $0 ) then
begin
require 'pipeline/os/file'
include Pipeline
files = OS::FindEx::find_files_recurse( 'x:/pipedev/tools/dcc/current/max2009/scripts/*.ms' )
analyser = ProjectUtil::MaxScriptFileDependencyAnalyser.new( )
dependencies = ProjectUtil::code_dependency_analyser( files, analyser )
dependencies.each do |filename, deps|
puts "#{filename} dependencies:"
deps.each do |dep|
puts "\t#{dep}"
end
end
puts "#{dependencies.size} files with dependencies."
rescue Exception => ex
puts "Unhandled exception: #{ex.message}"
puts ex.backtrace.join( "\n" )
end
end
# code_dependency_analyser.rb