199 lines
5.7 KiB
Ruby
Executable File
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
|