diff --git a/bin/cm b/bin/cm index 8cc463e29..04546d68b 100755 --- a/bin/cm +++ b/bin/cm @@ -14,6 +14,11 @@ desc 'Generate HTML documentation' long_desc 'Generate HTML docs from a Docurium config file' command :doc do |c| c.flag :for, :desc => "The version to generate", :multiple => true + c.switch [:n, "dry-run"], :desc => "Dry-run" + c.switch [:d, "debug"], :desc => "Enable debug log" + c.flag "debug-file", :desc => "Enable debug output for header", :multiple => true + c.flag "debug-function", :desc => "Show debug output when processing function", :multiple => true + c.flag "debug-type", :desc => "Show debug output when processing type", :multiple => true c.action do |global_options,options,args| file = args.first Docurium::CLI.doc(file, options) diff --git a/lib/docurium.rb b/lib/docurium.rb index a6932cd0e..300e1fb60 100644 --- a/lib/docurium.rb +++ b/lib/docurium.rb @@ -4,6 +4,7 @@ require 'rocco' require 'docurium/version' require 'docurium/layout' +require 'docurium/debug' require 'libdetect' require 'docurium/docparser' require 'pp' @@ -19,12 +20,13 @@ class Docurium attr_accessor :branch, :output_dir, :data, :head_data - def initialize(config_file, repo = nil) + def initialize(config_file, cli_options = {}, repo = nil) raise "You need to specify a config file" if !config_file raise "You need to specify a valid config file" if !valid_config(config_file) @sigs = {} @head_data = nil @repo = repo || Rugged::Repository.discover(config_file) + @cli_options = cli_options end def init_data(version = 'HEAD') @@ -192,9 +194,11 @@ def generate_docs(options) print "Generating documentation [#{i}/#{versions.count}]\r" - output_index.add(:path => "#{version}.json", :oid => sha, :mode => 0100644) - examples.each do |path, id| - output_index.add(:path => path, :oid => id, :mode => 0100644) + unless dry_run? + output_index.add(:path => "#{version}.json", :oid => sha, :mode => 0100644) + examples.each do |path, id| + output_index.add(:path => path, :oid => id, :mode => 0100644) + end end end @@ -203,6 +207,8 @@ def generate_docs(options) show_warnings(head_data) end + return if dry_run? + # We tally the signatures in the order they finished, which is # arbitrary due to the concurrency, so we need to sort them once # they've finished. @@ -391,7 +397,7 @@ def parse_headers(index, version) data = init_data(version) DocParser.with_files(files, :prefix => version) do |parser| headers.each do |header| - records = parser.parse_file(header) + records = parser.parse_file(header, debug: interesting?(:file, header)) update_globals!(data, records) end end @@ -466,16 +472,20 @@ def valid_config(file) def group_functions!(data) func = {} data[:functions].each_pair do |key, value| + debug_set interesting?(:function, key) + debug "grouping #{key}: #{value}" if @options['prefix'] k = key.gsub(@options['prefix'], '') else k = key end group, rest = k.split('_', 2) + debug "grouped: k: #{k}, group: #{group}, rest: #{rest}" if group.empty? puts "empty group for function #{key}" next end + debug "grouped: k: #{k}, group: #{group}, rest: #{rest}" data[:functions][key][:group] = group func[group] ||= [] func[group] << key @@ -521,6 +531,25 @@ def update_globals!(data, recs) md = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new({}), :no_intra_emphasis => true) recs.each do |r| + types = %w(function file type).map(&:to_sym) + dbg = false + types.each do |t| + dbg ||= if r[:type] == t and interesting?(t, r[:name]) + true + elsif t == :file and interesting?(:file, r[:file]) + true + elsif [:struct, :enum].include?(r[:type]) and interesting?(:type, r[:name]) + true + else + false + end + end + + debug_set dbg + + debug "processing record: #{r}" + debug + # initialize filemap for this file file_map[r[:file]] ||= { :file => r[:file], :functions => [], :meta => {}, :lines => 0 @@ -628,6 +657,10 @@ def update_globals!(data, recs) # Anything else we want to record? end + debug "processed record: #{r}" + debug + + debug_restore end data[:files] << file_map.values[0] @@ -658,4 +691,12 @@ def write_site(index) def out(text) puts text end + + def dry_run? + @cli_options[:dry_run] + end + + def interesting?(type, what) + @cli_options['debug'] || (@cli_options["debug-#{type}"] || []).include?(what) + end end diff --git a/lib/docurium/cli.rb b/lib/docurium/cli.rb index 13f209642..d22c36cd2 100644 --- a/lib/docurium/cli.rb +++ b/lib/docurium/cli.rb @@ -2,8 +2,8 @@ class Docurium class CLI def self.doc(idir, options) - doc = Docurium.new(idir) - doc.generate_docs(options) + doc = Docurium.new(idir, options) + doc.generate_docs end def self.check(idir, options) diff --git a/lib/docurium/debug.rb b/lib/docurium/debug.rb new file mode 100644 index 000000000..da3704891 --- /dev/null +++ b/lib/docurium/debug.rb @@ -0,0 +1,41 @@ +$debug_stack = [false] + +def debug_enabled + $debug_stack[-1] +end + +def debug(str = nil) + puts str if debug_enabled +end + +def debug_enable + $debug_stack.push true +end + +def debug_silence + $debug_stack.push false +end + +def debug_set val + $debug_stack.push val +end + +def debug_pass + $debug_stack.push debug_enabled +end + +def debug_restore + $debug_stack.pop +end + +def with_debug(&block) + debug_enable + block.call + debug_restore +end + +def without_debug(&block) + debug_silence + block.call + debug_restore +end \ No newline at end of file diff --git a/lib/docurium/docparser.rb b/lib/docurium/docparser.rb index f161b5d44..29edf05cb 100644 --- a/lib/docurium/docparser.rb +++ b/lib/docurium/docparser.rb @@ -59,12 +59,14 @@ def cleanup! # Entry point for this parser # Parse `filename` out of the hash `files` - def parse_file(orig_filename) + def parse_file(orig_filename, opts = {}) includes = find_clang_includes + [@tmpdir] # Override the path we want to filter by filename = File.join(@tmpdir, orig_filename) + debug_enable if opts[:debug] + debug "parsing #{filename} #{@tmpdir}" args = includes.map { |path| "-I#{path}" } args << '-ferror-limit=1' @@ -73,12 +75,13 @@ def parse_file(orig_filename) recs = [] tu.cursor.visit_children do |cursor, parent| - #puts "visiting #{cursor.kind} - #{cursor.spelling}" location = cursor.location next :continue if location.file == nil next :continue unless location.file == filename - #puts "for file #{location.file} #{cursor.kind} #{cursor.spelling} #{cursor.comment.kind} #{location.line}" + loc = "%d:%d-%d:%d" % [cursor.extent.start.line, cursor.extent.start.column, cursor.extent.end.line, cursor.extent.end.column] + debug "#{cursor.location.file}:#{loc} - visiting #{cursor.kind}: #{cursor.spelling}, comment is #{cursor.comment.kind}" + #cursor.visit_children do |c| # puts " child #{c.kind}, #{c.spelling}, #{c.comment.kind}" # :continue @@ -95,25 +98,38 @@ def parse_file(orig_filename) :tdef => nil, } - case cursor.kind + extract = case cursor.kind when :cursor_function - #puts "have function" - rec.merge! extract_function(cursor) + debug "have function #{cursor.spelling}" + rec.update extract_function(cursor) when :cursor_enum_decl - rec.merge! extract_enum(cursor) + debug "have enum #{cursor.spelling}" + rec.update extract_enum(cursor) when :cursor_struct - #puts "raw struct" - rec.merge! extract_struct(cursor) + debug "have struct #{cursor.spelling}" + rec.update extract_struct(cursor) when :cursor_typedef_decl - rec.merge! extract_typedef(cursor) + debug "have typedef #{cursor.spelling} #{cursor.underlying_type.spelling}" + rec.update extract_typedef(cursor) else raise "No idea how to deal with #{cursor.kind}" end + rec.merge! extract + recs << rec :continue end + if debug_enabled + puts "parse_file: parsed #{recs.size} records for #{filename}:" + recs.each do |r| + puts "\t#{r}" + end + end + + debug_restore + recs end @@ -187,15 +203,27 @@ def extract_function_args(cursor, cmt) def extract_subject_desc(comment) subject = comment.child.text + debug "\t\tsubject: #{subject}" paras = comment.find_all { |cmt| cmt.kind == :comment_paragraph }.drop(1).map { |p| p.text } desc = paras.join("\n\n") + debug "\t\tdesc: #{desc}" return subject, desc end def extract_function(cursor) comment = cursor.comment - #puts "looking at function #{cursor.spelling}, #{cursor.display_name}" + $buggy_functions = %w() + debug_set ($buggy_functions.include? cursor.spelling) + if debug_enabled + puts "\tlooking at function #{cursor.spelling}, #{cursor.display_name}" + puts "\tcomment: #{comment}, #{comment.kind}" + cursor.visit_children do |cur, parent| + puts "\t\tchild: #{cur.spelling}, #{cur.kind}" + :continue + end + end + cmt = extract_function_comment(comment) args = extract_function_args(cursor, cmt) #args = args.reject { |arg| arg[:comment].nil? } @@ -220,6 +248,7 @@ def extract_function(cursor) decl = "#{ret[:type]} #{cursor.spelling}(#{argline})" body = "#{decl};" + debug_restore #puts cursor.display_name # Return the format that docurium expects { @@ -238,6 +267,7 @@ def extract_function(cursor) def extract_function_comment(comment) subject, desc = extract_subject_desc(comment) + debug "\t\textract_function_comment: #{comment}, #{comment.kind}, #{subject}, #{desc}" args = {} (comment.find_all { |cmt| cmt.kind == :comment_param_command }).each do |param| @@ -313,7 +343,7 @@ def extract_struct(cursor) :continue end - #puts "struct value #{values}" + debug "\tstruct value #{values}" rec = { :type => :struct, diff --git a/test/docurium_test.rb b/test/docurium_test.rb index 48f48ed36..0cf1ed054 100644 --- a/test/docurium_test.rb +++ b/test/docurium_test.rb @@ -20,7 +20,7 @@ def setup end @path = File.dirname(__FILE__) + '/fixtures/git2/api.docurium' - @doc = Docurium.new(@path, @repo) + @doc = Docurium.new(@path, {}, @repo) @data = @doc.parse_headers(index, 'HEAD') end