Class: Kaitai::Struct::Visualizer::KSYCompiler
- Inherits:
-
Object
- Object
- Kaitai::Struct::Visualizer::KSYCompiler
- Defined in:
- lib/kaitai/struct/visualizer/ksy_compiler.rb
Instance Method Summary collapse
-
#compile_and_load(fns, code_dir) ⇒ String
Compiles Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler, and loads the generated Ruby files into current Ruby interpreter by running ‘require` on them.
-
#compile_formats(fns) ⇒ String
Compiles Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler, and loads the generated Ruby files into current Ruby interpreter by running ‘require` on them.
- #compile_formats_if(fns) ⇒ Object
-
#compile_formats_to_output(fns, code_dir) ⇒ Hash
Compiles Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler.
-
#initialize(opts, prog_name = 'ksv', out = $stderr) ⇒ KSYCompiler
constructor
Initializes a new instance of the KSYCompiler class that is used to compile Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler.
-
#load_ruby_files(fns, code_dir, log) ⇒ String
Loads Ruby files generated by kaitai-struct-compiler into current Ruby interpreter by running ‘require` on them.
- #psych_find(yaml, path_part) ⇒ Object
- #report_err(errs) ⇒ Object
-
#resolve_yaml_path(file, path) ⇒ Object
Parses YAML file using Ruby’s mid-level Psych API and resolve YAML path reported by ksc to row & column.
Constructor Details
#initialize(opts, prog_name = 'ksv', out = $stderr) ⇒ KSYCompiler
Initializes a new instance of the KSYCompiler class that is used to compile Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler.
29 30 31 32 33 34 35 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 29 def initialize(opts, prog_name = 'ksv', out = $stderr) @opts = opts @prog_name = prog_name @out = out @outdir = opts[:outdir] end |
Instance Method Details
#compile_and_load(fns, code_dir) ⇒ String
Compiles Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler, and loads the generated Ruby files into current Ruby interpreter by running ‘require` on them.
87 88 89 90 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 87 def compile_and_load(fns, code_dir) log = compile_formats_to_output(fns, code_dir) load_ruby_files(fns, code_dir, log) end |
#compile_formats(fns) ⇒ String
Compiles Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler, and loads the generated Ruby files into current Ruby interpreter by running ‘require` on them.
If the :outdir option was specified, the compiled code will be stored in that directory. Otherwise, a temporary directory will be used that will be deleted after the compilation and loading is done.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 63 def compile_formats(fns) if @outdir.nil? main_class_name = nil Dir.mktmpdir { |code_dir| main_class_name = compile_and_load(fns, code_dir) } else main_class_name = compile_and_load(fns, @outdir) end if main_class_name.nil? @out.puts 'Fatal errors encountered, cannot continue' exit 1 end main_class_name end |
#compile_formats_if(fns) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 37 def compile_formats_if(fns) return compile_formats(fns) if (fns.length > 1) || fns[0].end_with?('.ksy') fname = File.basename(fns[0], '.rb') dname = File.dirname(fns[0]) gpath = File.('*.rb', dname) Dir.glob(gpath) do |fn| require File.(fn, dname) end # The name of the main class is that of the given file by convention. fname.split('_').map(&:capitalize).join end |
#compile_formats_to_output(fns, code_dir) ⇒ Hash
Compiles Kaitai Struct formats into Ruby classes by invoking the command line kaitai-struct-compiler.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 99 def compile_formats_to_output(fns, code_dir) args = ['--ksc-json-output', '--debug', '-t', 'ruby', '-d', code_dir, *fns] # Extra arguments extra = [] extra += ['--import-path', @opts[:import_path]] if @opts[:import_path] extra += ['--opaque-types', @opts[:opaque_types]] if @opts[:opaque_types] args = extra + args # UNIX-based systems run ksc via a shell wrapper that requires # extra '--' in invocation to disambiguate our '-d' from java runner # '-d' (which allows to pass defines to JVM). Windows-based systems # do not need and do not support this extra '--', so we don't add it # on Windows. args.unshift('--') unless Kaitai::TUI.windows? begin log_str, err_str, status = Open3.capture3('kaitai-struct-compiler', *args) rescue Errno::ENOENT @out.puts "#{@prog_name}: unable to find and execute kaitai-struct-compiler in your PATH" exit 1 end unless status.success? if err_str =~ /Error: Unknown option --ksc-json-output/ @out.puts "#{@prog_name}: your kaitai-struct-compiler is too old:" system('kaitai-struct-compiler', '--version') @out.puts "\nPlease use at least v0.7." else @out.puts "ksc crashed (exit status = #{status}):\n" @out.puts "== STDOUT\n" @out.puts log_str @out.puts @out.puts "== STDERR\n" @out.puts err_str @out.puts end exit status.exitstatus end JSON.parse(log_str) end |
#load_ruby_files(fns, code_dir, log) ⇒ String
Loads Ruby files generated by kaitai-struct-compiler into current Ruby interpreter by running ‘require` on them.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 149 def load_ruby_files(fns, code_dir, log) errs = false main_class_name = nil fns.each_with_index do |fn, idx| log_fn = log[fn] if log_fn['errors'] report_err(log_fn['errors']) errs = true else log_classes = log_fn['output']['ruby'] log_classes.each_pair do |_k, v| if v['errors'] report_err(v['errors']) errs = true else compiled_name = v['files'][0]['fileName'] compiled_path = File.join(code_dir, compiled_name) require compiled_path end end # Is it main ClassSpecs? if idx.zero? main = log_classes[log_fn['firstSpecName']] main_class_name = main['topLevelName'] end end end errs ? nil : main_class_name end |
#psych_find(yaml, path_part) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 227 def psych_find(yaml, path_part) if yaml.is_a?(Psych::Nodes::Mapping) # mapping are key-values, which are represented as [k1, v1, k2, v2, ...] yaml.children.each_slice(2) do |map_key, map_value| return map_value if map_key.value == path_part end nil elsif yaml.is_a?(Psych::Nodes::Sequence) # sequences are just integer-indexed arrays - [a0, a1, a2, ...] idx = Integer(path_part) yaml.children[idx] else raise "Unknown Psych component encountered: #{yaml.class}" end end |
#report_err(errs) ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 183 def report_err(errs) @out.puts((errs.length > 1 ? 'Errors' : 'Error') + ":\n\n") errs.each do |err| @out << err['file'] row = err['line'] col = err['col'] if row.nil? && err['path'] begin node = resolve_yaml_path(err['file'], err['path']) # Psych line numbers are 0-based, but we want 1-based row = node.start_line + 1 # Psych column numbers are 0-based, but we want 1-based col = node.start_column + 1 rescue StandardError row = '!' col = '!' end end if row @out << ':' << row @out << ':' << col if col end @out << ':/' << err['path'].join('/') if err['path'] @out << ': ' << err['message'] << "\n" end end |
#resolve_yaml_path(file, path) ⇒ Object
Parses YAML file using Ruby’s mid-level Psych API and resolve YAML path reported by ksc to row & column.
218 219 220 221 222 223 224 225 |
# File 'lib/kaitai/struct/visualizer/ksy_compiler.rb', line 218 def resolve_yaml_path(file, path) doc = Psych.parse(File.read(file)) yaml = doc.children[0] path.each do |path_part| yaml = psych_find(yaml, path_part) end yaml end |