diff --git a/lib/asciidoctor-diagram/blockdiag/extension.rb b/lib/asciidoctor-diagram/blockdiag/extension.rb index 781e65f9..f826dc77 100644 --- a/lib/asciidoctor-diagram/blockdiag/extension.rb +++ b/lib/asciidoctor-diagram/blockdiag/extension.rb @@ -96,6 +96,7 @@ def self.define_processors(name) ::Asciidoctor::Diagram.const_set("#{name}BlockMacroProcessor", block_macro) end + include CliGenerator include Which def blockdiag(tool, parent, source, format) @@ -105,7 +106,7 @@ def blockdiag(tool, parent, source, format) # a '3' suffix. alt_cmd_name = "#{tool.downcase}3" - CliGenerator.generate_stdin(which(parent, cmd_name, :alt_cmds => [alt_cmd_name]), format.to_s, source.to_s) do |tool_path, output_path| + generate_stdin(which(parent, cmd_name, :alt_cmds => [alt_cmd_name]), format.to_s, source.to_s) do |tool_path, output_path| [tool_path, '-a', '-o', Platform.native_path(output_path), "-T#{format.to_s}", '-'] end end diff --git a/lib/asciidoctor-diagram/erd/extension.rb b/lib/asciidoctor-diagram/erd/extension.rb index b7809517..27136d4a 100644 --- a/lib/asciidoctor-diagram/erd/extension.rb +++ b/lib/asciidoctor-diagram/erd/extension.rb @@ -7,6 +7,7 @@ module Asciidoctor module Diagram # @private module Erd + include CliGenerator include Which def self.included(mod) @@ -21,11 +22,11 @@ def erd(parent, source, format) erd_path = which(parent, 'erd') dot_path = which(parent, 'dot', :alt_attrs => ['graphvizdot']) - dot_code = CliGenerator.generate_stdin(erd_path, format.to_s, source.to_s) do |tool_path, output_path| + dot_code = generate_stdin(erd_path, format.to_s, source.to_s) do |tool_path, output_path| [tool_path, '-o', Platform.native_path(output_path), '-f', 'dot'] end - CliGenerator.generate_stdin(dot_path, format.to_s, dot_code) do |tool_path, output_path| + generate_stdin(dot_path, format.to_s, dot_code) do |tool_path, output_path| [tool_path, "-o#{Platform.native_path(output_path)}", "-T#{format.to_s}"] end end diff --git a/lib/asciidoctor-diagram/extensions.rb b/lib/asciidoctor-diagram/extensions.rb index 93bf0c64..889aa145 100644 --- a/lib/asciidoctor-diagram/extensions.rb +++ b/lib/asciidoctor-diagram/extensions.rb @@ -115,7 +115,11 @@ def process(parent, reader_or_target, attributes) raise e else text = "Failed to generate image: #{e.message}" - warn %(asciidoctor-diagram: ERROR: #{text}) + warn_msg = text.dup + if $VERBOSE + warn_msg << "\n" << e.backtrace.join("\n") + end + warn %(asciidoctor-diagram: ERROR: #{warn_msg}) text << "\n" text << source.code Asciidoctor::Block.new parent, :listing, :source => text, :attributes => attributes diff --git a/lib/asciidoctor-diagram/graphviz/extension.rb b/lib/asciidoctor-diagram/graphviz/extension.rb index 0ae83886..36d589e7 100644 --- a/lib/asciidoctor-diagram/graphviz/extension.rb +++ b/lib/asciidoctor-diagram/graphviz/extension.rb @@ -7,6 +7,7 @@ module Asciidoctor module Diagram # @private module Graphviz + include CliGenerator include Which def self.included(mod) @@ -18,7 +19,7 @@ def self.included(mod) end def graphviz(parent, source, format) - CliGenerator.generate_stdin(which(parent, 'dot', :alt_attrs => ['graphvizdot']), format.to_s, source.to_s) do |tool_path, output_path| + generate_stdin(which(parent, 'dot', :alt_attrs => ['graphvizdot']), format.to_s, source.to_s) do |tool_path, output_path| args = [tool_path, "-o#{Platform.native_path(output_path)}", "-T#{format.to_s}"] layout = source.attr('layout') diff --git a/lib/asciidoctor-diagram/meme/extension.rb b/lib/asciidoctor-diagram/meme/extension.rb index 84096466..facf7245 100644 --- a/lib/asciidoctor-diagram/meme/extension.rb +++ b/lib/asciidoctor-diagram/meme/extension.rb @@ -36,7 +36,7 @@ def meme(p, c, format) options = c.attr('options', '').split(',') noupcase = options.include?('noupcase') - dimensions = CliGenerator.run_cli(identify, '-format', '%w %h', bg_img).match(/(?\d+) (?\d+)/) + dimensions = Cli.run(identify, '-format', '%w %h', bg_img).match(/(?\d+) (?\d+)/) bg_width = dimensions['w'].to_i bg_height = dimensions['h'].to_i label_width = bg_width @@ -44,7 +44,7 @@ def meme(p, c, format) if top_label top_img = Tempfile.new(['meme', '.png']) - CliGenerator.run_cli( + Cli.run( convert, '-background', 'none', '-fill', fill_color, @@ -62,7 +62,7 @@ def meme(p, c, format) if bottom_label bottom_img = Tempfile.new(['meme', '.png']) - CliGenerator.run_cli( + Cli.run( convert, '-background', 'none', '-fill', fill_color, @@ -91,7 +91,7 @@ def meme(p, c, format) args << final_img.path - CliGenerator.run_cli(*args) + Cli.run(*args) File.binread(final_img) end diff --git a/lib/asciidoctor-diagram/mermaid/extension.rb b/lib/asciidoctor-diagram/mermaid/extension.rb index d870edb2..8c767793 100644 --- a/lib/asciidoctor-diagram/mermaid/extension.rb +++ b/lib/asciidoctor-diagram/mermaid/extension.rb @@ -1,4 +1,5 @@ require_relative '../extensions' +require_relative '../util/cli' require_relative '../util/cli_generator' require_relative '../util/platform' require_relative '../util/which' @@ -7,6 +8,7 @@ module Asciidoctor module Diagram # @private module Mermaid + include CliGenerator include Which def self.included(mod) @@ -19,7 +21,7 @@ def self.included(mod) def mermaid(parent, source, format) mermaid = which(parent, 'mermaid') - @is_mermaid_v6 ||= `#{mermaid} --version`.split('.')[0].to_i >= 6 + @is_mermaid_v6 ||= ::Asciidoctor::Diagram::Cli.run(mermaid, '--version').split('.')[0].to_i >= 6 # Mermaid >= 6.0.0 requires PhantomJS 2.1; older version required 1.9 phantomjs = which(parent, 'phantomjs', :alt_attrs => [@is_mermaid_v6 ? 'phantomjs_2' : 'phantomjs_19']) @@ -30,7 +32,7 @@ def mermaid(parent, source, format) width = source.attr('width') - CliGenerator.generate_file(mermaid, 'mmd', format.to_s, source.to_s) do |tool_path, input_path, output_path| + generate_file(mermaid, 'mmd', format.to_s, source.to_s) do |tool_path, input_path, output_path| output_dir = File.dirname(output_path) output_file = File.expand_path(File.basename(input_path) + ".#{format.to_s}", output_dir) diff --git a/lib/asciidoctor-diagram/shaape/extension.rb b/lib/asciidoctor-diagram/shaape/extension.rb index 200685e5..e4591af7 100644 --- a/lib/asciidoctor-diagram/shaape/extension.rb +++ b/lib/asciidoctor-diagram/shaape/extension.rb @@ -7,6 +7,7 @@ module Asciidoctor module Diagram # @private module Shaape + include CliGenerator include Which def self.included(mod) @@ -18,7 +19,7 @@ def self.included(mod) end def shaape(parent, source, format) - CliGenerator.generate_stdin(which(parent, 'shaape'), format.to_s, source.to_s) do |tool_path, output_path| + generate_stdin(which(parent, 'shaape'), format.to_s, source.to_s) do |tool_path, output_path| [tool_path, '-o', Platform.native_path(output_path), '-t', format.to_s, '-'] end end diff --git a/lib/asciidoctor-diagram/util/cli.rb b/lib/asciidoctor-diagram/util/cli.rb new file mode 100644 index 00000000..ad8a818a --- /dev/null +++ b/lib/asciidoctor-diagram/util/cli.rb @@ -0,0 +1,19 @@ +require 'tempfile' +require 'open3' + +module Asciidoctor + module Diagram + # @private + module Cli + def self.run(*args) + stdout, stderr, status = Open3.capture3(*args) + + if status != 0 + raise "#{File.basename(args[0])} failed: #{stdout.empty? ? stderr : stdout}" + end + + stdout.empty? ? stderr : stdout + end + end + end +end diff --git a/lib/asciidoctor-diagram/util/cli_generator.rb b/lib/asciidoctor-diagram/util/cli_generator.rb index a455be9c..12d6a753 100644 --- a/lib/asciidoctor-diagram/util/cli_generator.rb +++ b/lib/asciidoctor-diagram/util/cli_generator.rb @@ -1,11 +1,11 @@ require 'tempfile' -require 'open3' +require_relative 'cli' module Asciidoctor module Diagram # @private module CliGenerator - def self.generate_stdin(tool, format, code) + def generate_stdin(tool, format, code) tool_name = File.basename(tool) target_file = Tempfile.new([tool_name, ".#{format}"]) @@ -20,7 +20,7 @@ def self.generate_stdin(tool, format, code) end end - def self.generate_file(tool, input_ext, output_ext, code) + def generate_file(tool, input_ext, output_ext, code) tool_name = File.basename(tool) source_file = Tempfile.new([tool_name, ".#{input_ext}"]) @@ -42,7 +42,7 @@ def self.generate_file(tool, input_ext, output_ext, code) end end - def self.generate(opts, target_file, open3_opts = {}) + def generate(opts, target_file, open3_opts = {}) case opts when Array args = opts @@ -54,7 +54,7 @@ def self.generate(opts, target_file, open3_opts = {}) raise "Block passed to generate_file should return an Array or a Hash" end - output = run_cli(*args, open3_opts) + output = ::Asciidoctor::Diagram::Cli.run(*args, open3_opts) raise "#{args[0]} failed: #{output}" unless File.exist?(out_file || target_file.path) @@ -64,16 +64,6 @@ def self.generate(opts, target_file, open3_opts = {}) File.binread(target_file.path) end - - def self.run_cli(*args) - stdout, stderr, status = Open3.capture3(*args) - - if status != 0 - raise "#{File.basename(args[0])} failed: #{stdout.empty? ? stderr : stdout}" - end - - stdout.empty? ? stderr : stdout - end end end end diff --git a/lib/asciidoctor-diagram/util/java.rb b/lib/asciidoctor-diagram/util/java.rb index 1d13c851..7057144f 100644 --- a/lib/asciidoctor-diagram/util/java.rb +++ b/lib/asciidoctor-diagram/util/java.rb @@ -78,7 +78,7 @@ def self.create_error(prefix_msg, response) if content_type.start_with? 'application/json' json = JSON.parse(response[:body].force_encoding(Encoding::UTF_8)) ruby_bt = Kernel.caller(2) - java_bt = json['stk'].map { |java_line| "#{java_line[0]}:#{java_line[3]}: in `#{java_line[2]}'" } + java_bt = json['stk'].map { |java_line| "#{java_line[0]}:#{java_line[3]}: in '#{java_line[2]}'" } error = RuntimeError.new("#{prefix_msg}: #{json['msg']}") error.set_backtrace java_bt + ruby_bt raise error diff --git a/lib/asciidoctor-diagram/util/java_socket.rb b/lib/asciidoctor-diagram/util/java_socket.rb index 76455980..c041cff6 100644 --- a/lib/asciidoctor-diagram/util/java_socket.rb +++ b/lib/asciidoctor-diagram/util/java_socket.rb @@ -1,6 +1,7 @@ require 'socket' -require 'rbconfig' +require_relative 'cli' +require_relative 'platform' require_relative 'which' module Asciidoctor @@ -15,10 +16,10 @@ def initialize(java, classpath) args << '-Djava.awt.headless=true' args << '-cp' # special case for cygwin, it requires path translation for java to work - if RbConfig::CONFIG['host_os'] =~ /cygwin/i + if ::Asciidoctor::Diagram::Platform.os_variant == :cygwin cygpath = ::Asciidoctor::Diagram::Which.which('cygpath') - if(cygpath != nil) - args << classpath.flatten.map { |jar| `cygpath -w "#{jar}"`.strip }.join(";") + if cygpath != nil + args << classpath.flatten.map { |jar| ::Asciidoctor::Diagram::Cli.run(cygpath, '-w', jar).strip }.join(";") else puts 'cygwin warning: cygpath not found' args << classpath.flatten.join(File::PATH_SEPARATOR) @@ -77,15 +78,13 @@ def self.send_request(req) private def self.find_java - if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM - # Windows - path_to(ENV['JAVA_HOME'], 'bin/java.exe') || registry_lookup || ::Asciidoctor::Diagram::Which.which('java') - elsif /darwin/ =~ RUBY_PLATFORM - # Mac - path_to(ENV['JAVA_HOME'], 'bin/java') || path_to(`/usr/libexec/java_home`.strip, 'bin/java') || ::Asciidoctor::Diagram::Which.which('java') - else - # Other unix-like system - path_to(ENV['JAVA_HOME'], 'bin/java') || ::Asciidoctor::Diagram::Which.which('java') + case ::Asciidoctor::Diagram::Platform.os + when :windows + path_to(ENV['JAVA_HOME'], 'bin/java.exe') || registry_lookup || ::Asciidoctor::Diagram::Which.which('java') + when :macosx + path_to(ENV['JAVA_HOME'], 'bin/java') || path_to(::Asciidoctor::Diagram::Cli.run('/usr/libexec/java_home').strip, 'bin/java') || ::Asciidoctor::Diagram::Which.which('java') + else + path_to(ENV['JAVA_HOME'], 'bin/java') || ::Asciidoctor::Diagram::Which.which('java') end end @@ -98,21 +97,90 @@ def self.path_to(java_home, java_binary) end end + JDK_KEY = 'HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit' + JRE_KEY = 'HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment' + def self.registry_lookup - key_re = /^HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\.*\\([0-9\.]+)/ - value_re = /\s*JavaHome\s*REG_SZ\s*(.*)/ - result = `reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft" /s /v JavaHome`.lines.map { |l| l.strip } - vms = result.each_slice(3).map do |_, key, value| - key_match = key_re.match(key) - value_match = value_re.match(value) - if key_match && value_match - [key_match[1].split('.').map { |v| v.to_i }, value_match[1]] - else - nil + registry_current(JRE_KEY) || registry_current(JDK_KEY) || registry_any() + end + + def self.registry_current(key) + current_version = registry_query(key, 'CurrentVersion') + if current_version + java_home = registry_query("#{key}\\#{current_version}", 'JavaHome') + java_exe(java_home) + else + nil + end + end + + def self.registry_any() + java_homes = registry_query('HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft', 'JavaHome', :recursive => true).values + java_homes.map { |path| java_exe(path) }.find { |exe| !exe.nil? } + end + + def self.java_exe(java_home) + java = File.expand_path('bin/java.exe', java_home) + + if File.executable?(java) + java + else + nil + end + end + + def self.registry_query(key, value = nil, opts = {}) + args = ['reg', 'query'] + args << key + args << '/v' << value unless value.nil? + args << '/s' if opts[:recursive] + + begin + lines = ::Asciidoctor::Diagram::Cli.run(*args).lines.reject { |l| l.strip.empty? }.each + rescue + lines = [].each + end + + result = {} + + while true + begin + begin + k = lines.next + rescue StopIteration + break + end + + unless k.start_with? key + next + end + + v = nil + begin + v = lines.next.strip if lines.peek.start_with?(' ') + rescue StopIteration + break + end + + if !k.valid_encoding? || (v && !v.valid_encoding?) + next + end + + if v && (md = /([^\s]+)\s+(REG_[^\s]+)\s+(.+)/.match(v)) + v_name = md[1] + v_value = md[3] + result["#{k}\\#{v_name}"] = v_value + else + result[k] = v + end end - end.reject { |v| v.nil? }.sort_by { |v| v[0] } - java_exes = vms.map { |version, path| File.expand_path('bin/java.exe', path) }.select { |exe| File.executable?(exe) } - java_exes && java_exes[0] + end + + if value && !opts[:recursive] + result.values[0] + else + result + end end end end diff --git a/lib/asciidoctor-diagram/util/platform.rb b/lib/asciidoctor-diagram/util/platform.rb index f1d436a1..d99e6b2b 100644 --- a/lib/asciidoctor-diagram/util/platform.rb +++ b/lib/asciidoctor-diagram/util/platform.rb @@ -7,6 +7,10 @@ def self.os os_info[:os] end + def self.os_variant + os_info[:os_variant] + end + def self.path_separator os_info[:path_sep] end @@ -14,20 +18,27 @@ def self.path_separator def self.os_info @os ||= ( host_os = RbConfig::CONFIG['host_os'] + + path_sep = '/' + variant = nil case host_os - when /mswin|bccwin|wince|emc/ - {:os => :windows, :path_sep => '\\'} - when /msys|mingw|cygwin/ - {:os => :windows, :path_sep => '/'} - when /darwin|mac os/ - {:os => :macosx, :path_sep => '/'} - when /linux/ - {:os => :linux, :path_sep => '/'} - when /solaris|bsd/ - {:os => :unix, :path_sep => '/'} + when /(mswin|bccwin|wince|emc)/i + os = :windows + variant = $1.downcase.to_sym + path_sep = '\\' + when /(msys|mingw|cygwin)/i + os = :windows + variant = $1.downcase.to_sym + when /darwin|mac os/i + os = :macosx + when /linux/i + os = :linux else - raise Error::WebDriverError, "unknown os: #{host_os.inspect}" + os = :unix end + { + :os => os, :os_variant => variant || os, :path_sep => path_sep + } ) end diff --git a/lib/asciidoctor-diagram/wavedrom/extension.rb b/lib/asciidoctor-diagram/wavedrom/extension.rb index 49718b01..ee060f5a 100644 --- a/lib/asciidoctor-diagram/wavedrom/extension.rb +++ b/lib/asciidoctor-diagram/wavedrom/extension.rb @@ -7,6 +7,7 @@ module Asciidoctor module Diagram # @private module Wavedrom + include CliGenerator include Which def self.included(mod) @@ -22,7 +23,7 @@ def wavedrom(parent, source, format) phantomjs = which(parent, 'phantomjs', :alt_attrs => ['phantomjs_2'], :raise_on_error => false) if wavedrom_cli && !wavedrom_cli.include?('WaveDromEditor') && phantomjs - CliGenerator.generate_file(wavedrom_cli, 'wvd', format.to_s, source.to_s) do |tool_path, input_path, output_path| + generate_file(wavedrom_cli, 'wvd', format.to_s, source.to_s) do |tool_path, input_path, output_path| [phantomjs, Platform.native_path(tool_path), '-i', Platform.native_path(input_path), "-#{format.to_s[0]}", Platform.native_path(output_path)] end else @@ -35,7 +36,7 @@ def wavedrom(parent, source, format) wavedrom = which(parent, 'WaveDromEditor', :alt_attrs => ['wavedrom']) end - CliGenerator.generate_file(wavedrom, 'wvd', format.to_s, source.to_s) do |tool_path, input_path, output_path| + generate_file(wavedrom, 'wvd', format.to_s, source.to_s) do |tool_path, input_path, output_path| [tool_path, 'source', Platform.native_path(input_path), format.to_s, Platform.native_path(output_path)] end end diff --git a/spec/plantuml_spec.rb b/spec/plantuml_spec.rb index aaf506bf..25af6a53 100644 --- a/spec/plantuml_spec.rb +++ b/spec/plantuml_spec.rb @@ -37,6 +37,42 @@ expect(b.attributes['height']).to_not be_nil end + it "should generate PNG images when format is set to 'png'" do + code = <<-eos +User -> (Start) +User --> (Use the application) : Label + +:Main Admin: ---> (Use the application) : Another label + eos + + File.write('plantuml.txt', code) + + doc = <<-eos += Hello, PlantUML! +Doc Writer + +== First Section + +plantuml::plantuml.txt[format="png"] + eos + + d = load_asciidoc doc + expect(d).to_not be_nil + + b = d.find { |bl| bl.context == :image } + expect(b).to_not be_nil + + expect(b.content_model).to eq :empty + + target = b.attributes['target'] + expect(target).to_not be_nil + expect(target).to match(/\.png$/) + expect(File.exists?(target)).to be true + + expect(b.attributes['width']).to_not be_nil + expect(b.attributes['height']).to_not be_nil + end + it 'should support substitutions' do code = <<-eos class {parent-class} diff --git a/spec/test_helper.rb b/spec/test_helper.rb index 511aed87..e8f67a92 100644 --- a/spec/test_helper.rb +++ b/spec/test_helper.rb @@ -77,10 +77,10 @@ def load_asciidoc(source, options = {}) c.around(:each) do |example| metadata = example.metadata group_dir = File.expand_path(metadata[:example_group][:full_description].gsub(/[^\w]+/, '_'), TEST_DIR) - Dir.mkdir(group_dir) unless Dir.exist?(group_dir) + FileUtils.mkdir_p(group_dir) unless Dir.exist?(group_dir) test_dir = File.expand_path(metadata[:description].gsub(/[^\w]+/, '_'), group_dir) - Dir.mkdir(test_dir) + FileUtils.mkdir_p(test_dir) unless Dir.exist?(test_dir) Dir.chdir(test_dir) do example.run