# # File:: file_get_usage.rb # Description:: Functions for getting file usage. # # Author:: Marissa Warner-Wu # Date:: 5 August 2009 # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require 'pp' require 'pipeline/os/file' require 'pipeline/resourcing/path' require 'pipeline/util/maxscript' require "rexml/document" include REXML #----------------------------------------------------------------------------- # Implementation #----------------------------------------------------------------------------- module Pipeline module ProjectUtil #------------------------------------------------------------------------- # Functions #------------------------------------------------------------------------- # # == Description # A function which determines the usage on MaxScript files in a given directory. # # Generates a hash which prints to the screen or to an optional output file, if given. # # === Example Usage # Pretty much what it says on the box. # # g_ToolsRoot = "x:/pipedev/" # g_OutputFile = "x:/usage.txt" # FileUsageHash::file_get_usage_maxscript( g_ToolsRoot, g_OutputFile ) # def ProjectUtil::file_get_usage_maxscript( toolsroot, outputFile ) # Generate the usage hash and related warnings usage_hash = {} bad_refs = {} FileUsageHash::create_hash_maxscript( toolsroot ) do |newHash, warnings| usage_hash = newhash bad_refs = warnings end # Generate output if outputFile.nil? FileUsageHash::pp( usage_hash ) else case OS::Path::get_extension( outputFile ) when 'txt' then FileUsageHash::to_txt( usage_hash, outputFile, bad_refs ) when 'xml' then FileUsageHash::to_xml( usage_hash, outputFile ) else puts "ERROR: This file type is not supported, could not create output file." end #case OS::Path::get_extension( outputFile ) end #if outputFile.nil? end #file_get_usage_maxscript # # == Description # Similar to file_get_usage_maxscript(), except that it exhaustively attempts to # find unused files by checking function and rollout calls. # # Generates a hash which prints to the screen or to an optional output file, if given # (in this case it will only print out the unused items). # # === Example Usage # Pretty much what it says on the box. # # g_ToolsRoot = "x:/pipedev/" # g_OutputFile = "x:/usage.txt" # FileUsageHash::file_get_unused_maxscript( g_ToolsRoot, g_OutputFile ) # def ProjectUtil::file_get_unused_maxscript( toolsroot, outputFile ) # Generate the usage hash and related warnings usage_hash = {} bad_refs = {} puts "Creating the usage hash..." FileUsageHash::create_hash_maxscript( toolsroot ) do |newHash, warnings| usage_hash = newHash bad_refs = warnings end # Cull out items by seeing what is really used puts "Checking usage against function calls..." usage_hash.each do |script, references| # Don't check the settings next if OS::Path::get_filename( script ) == 'settings.ms' # Start with the usage as false used = false scriptpath = Maxscript::full_path( script, toolsroot ) puts "CHECKING: #{scriptpath}" references.each_key do |file| # Don't check this file if it's the menu next if OS::Path::get_filename( file ) == 'rsutils.mcr' # Don't check global references next if file.eql? "global" # Get the usage used = Maxscript::file_is_used?( scriptpath, file ) # If we couldn't find it then do a global check unless used partpath = Maxscript::format_path( file ) if usage_hash.has_key? partpath includes = usage_hash[partpath] includes.each_key do |included_file| # Don't check global references next if included_file.eql? "global" # Otherwise see if it's used used = Maxscript::file_is_used?( scriptpath, included_file ) break if used == true end #includes.each end #if usage_hash.has_key? # If the file is still unused, mark it unless used # Create warning warning = "Was included in #{file} (line: " references[file].each{ |line_number| warning << line_number.to_s << " " } warning << ") but no functions or rollouts were called" if bad_refs.has_key? scriptpath bad_refs[scriptpath] << warning else bad_refs[scriptpath] = [warning] end # Remove from it from the list of referenced files references.delete( file ) end #unless used end #includes.each end #references.each_key # If we still can't find a use for the file, check global function calls unless used usage_hash.each_key do |file| # Don't check the file itself next if script.eql? file # Otherwise see if it's used filepath = Maxscript::full_path( file, toolsroot ) used = Maxscript::file_is_used?( scriptpath, filepath ) break if used == true end #usage_hash.each_key # Add a global reference references['global'] = [0] if used end #unless used end #usage_hash.each # Generate output if outputFile.nil? FileUsageHash::pp( usage_hash ) else puts "Creating output file..." case OS::Path::get_extension( outputFile ) when 'txt' then FileUsageHash::to_txt_unused( usage_hash, outputFile, bad_refs ) when 'xml' then FileUsageHash::to_xml_unused( usage_hash, outputFile ) else puts "ERROR: This file type is not supported, could not create output file." end #case OS::Path::get_extension( outputFile ) end #if outputFile.nil? end #file_get_usage_maxscript end # ProjectUtil module # # == Description # Small class for functions which create and manipulate usage hashes for sets of files. # The hash comes in the following form: # # class FileUsageHash #--------------------------------------------------------------------- # Class Methods #--------------------------------------------------------------------- # # == Description # A function which determines the usage on MaxScript files in a given directory. # Yields the hash and a hash of warnings to an optional given block. # # === Example Usage # Pretty much what it says on the box. # # g_ToolsRoot = "x:/pipedev/" # FileUsageHash::create_hash_maxscript( g_ToolsRoot ) # def FileUsageHash::create_hash_maxscript( toolsroot, &block ) # Get all the maxscript files ms_files = Maxscript::find_files( toolsroot ) # Create the hash file_hash = {} ms_files.each do |filepath| # Add to the hash unless this is a startup script, which we can safely skip part_path = Maxscript::format_path( filepath ) file_hash[part_path] = {} unless OS::Path::get_trailing_directory( part_path ) == "startup" end # Add the menus to the file list rsutils = OS::Path::combine( toolsroot, Maxscript::dir_max, "ui/macroscripts/rsutils.mcr" ) ms_files << rsutils # Keep track of bad references bad_refs = {} # Search through the files ms_files.each do |scriptfile| puts "USAGE: #{scriptfile}" # Get the contents of this file text = Maxscript::get_lines( scriptfile ) # Parse each line of text text.each_index do |index| line = text[index] line_number = index + 1 # Try to get the filename filename = Maxscript::get_included_file( line, scriptfile ) unless filename.empty? # Try to find this file in the hash if file_hash.has_key?(filename) # Add this script file to the list of places this file was used refs = file_hash[filename] if refs.has_key?(scriptfile) refs[scriptfile] << line_number else refs[scriptfile] = [line_number] end else # Store a warning if the file wasn't found unless this is a comment unless Maxscript::line_is_comment?( line ) warning = "#{filename} in the following line: \"#{line.chomp}\"" if bad_refs.has_key? scriptfile bad_refs[scriptfile] << warning else bad_refs[scriptfile] = [warning] end end #unless Maxscript::line_is_comment? end #if file_hash.has_key? end #unless filename.empty? end #text.each end #ms_files.each do |scriptfile| yield( file_hash, bad_refs ) if block_given? end #create_hash_maxscript # # == Description # A specialised helper function which pretty prints the Get Usage hash information. # # === Example Usage # Fairly self-explanatory, only really useful for debugging. Usually you would want # to print to a file as in below. # def FileUsageHash::pp( usage_hash ) usage_hash.each do |file, references| puts "---------------- #{file}:" pp references end end #pp # # == Description # A specialised helper function which prints the Get Usage hash information to a .txt # file. If given a hash of bad references it will also format these and print out # the information at the end of the file. # # === Example Usage # MWW TODO # def FileUsageHash::to_txt( usage_hash, outputFile, bad_refs={} ) ::FileUtils::rm( outputFile ) if ( File.exist?( outputFile ) ) log = File.new(outputFile, 'w') log.write("=== MAXSCRIPT FILE USAGE ===\n\n") # Print out information for each item in the hash keys = usage_hash.keys keys.sort! keys.each do |script_name| usage_info = usage_hash[script_name] log.write("----#{script_name}:\n") if usage_info.empty? log.write("[[UNUSED]]\n\n") else usage_info.each do |file, lines| log.write("\t\t#{file} used this at line(s): ") lines.each { |number| log.write( "#{number.to_s} " ) } log.write("\n") end #usage_info.each log.write("\n") end end #file_hash.each # Print out bad refs warning unless bad_refs.empty? log.write("============= Found the following possible bad refs:\n") bad_refs.each do |file_name, warnings| log.write("----#{file_name}:\n") warnings.each { |ref| log.write("\t\t#{ref}\n") } log.write("\n") end #bad_refs.each end #unless bad_refs.empty? log.close() puts "Wrote output file to #{outputFile}." end #to_txt # # == Description # The same as to_txt(), but only prints out information for unused files. If given # a hash of bad references it will also format these and print out the information # at the end of the file. # # === Example Usage # MWW TODO # def FileUsageHash::to_txt_unused( usage_hash, outputFile, bad_refs={} ) ::FileUtils::rm( outputFile ) if ( File.exist?( outputFile ) ) log = File.new(outputFile, 'w') log.write("=== MAXSCRIPT FILE USAGE -- UNUSED ===\n\n") # Print out information for each item in the hash keys = usage_hash.keys keys.sort! keys.each do |script_name| usage_info = usage_hash[script_name] log.write("#{script_name}\n") if usage_info.empty? end #usage_hash.each log.write("\n") # Print out bad refs warning unless bad_refs.empty? log.write("============= Found the following possible bad refs:\n") bad_refs.each do |file_name, warnings| log.write("----#{file_name}:\n") warnings.each { |ref| log.write("\t\t#{ref}\n") } log.write("\n") end #bad_refs.each end #unless bad_refs.empty? log.close() puts "Wrote output file to #{outputFile}" end #to_txt_unused # # == Description # A specialised helper function which prints the Get Usage hash information to a .xml # file. If no output file is given then it passes back the REXML Document object. # # Note that this function does not take or print any of the bad reference warnings. # To print this information, use to_txt() or to_txt_unused(). # # === Example Usage # MWW TODO # def FileUsageHash::to_xml( usage_hash, outputFile ) ::FileUtils::rm( outputFile ) if ( File.exist?( outputFile ) ) # Create document log = Document.new log << XMLDecl.new log << Comment.new( "MAXSCRIPT FILE USAGE" ) root = Element.new( 'Usage' ) # Create elements for each file keys = usage_hash.keys keys.sort! keys.each do |file| references = usage_hash[file] file_elem = root.add_element( 'file' ) file_elem.add_attribute( 'name', file ) references.each do |ref_file, lines| ref_elem = file_elem.add_element( 'ref_file' ) ref_elem.add_attribute( 'name', ref_file ) lines.each do |number| line_elem = ref_elem.add_element( 'line' ) line_elem.add_text( number.to_s ) end end #references.each end #usage_hash.each log << root # Write the XML or pass back the document if outputFile.nil? return log else File.open( outputFile, 'w' ) do |file| fmt = REXML::Formatters::Pretty.new() fmt.write( log, file ) end end #if outputFile.nil? end #to_xml # # == Description # The same as to_xml(), but only prints out information for unused items. If no output # file is given then it passes back the REXML Document object. # # Note that this function does not take or print any of the bad reference warnings. # To print this information, use to_txt() or to_txt_unused(). # # === Example Usage # MWW TODO # def FileUsageHash::to_xml_unused( usage_hash, outputFile ) ::FileUtils::rm( outputFile ) if ( File.exist?( outputFile ) ) # Create document log = Document.new log << XMLDecl.new log << Comment.new( "MAXSCRIPT FILE USAGE -- UNUSED" ) root = Element.new( 'Usage' ) # Create elements for each file keys = usage_hash.keys keys.sort! keys.each do |file| references = usage_hash[file] if references.empty? file_elem = root.add_element( 'file' ) file_elem.add_attribute( 'name', file ) end end #usage_hash.each log << root # Write the XML or pass back the document if outputFile.nil? return log else File.open( outputFile, 'w' ) do |file| fmt = REXML::Formatters::Pretty.new() fmt.write( log, file ) end end #if outputFile.nil? end #to_xml_unused end #FileUsageHash end # Pipeline module # file_get_usage.rb