htslib 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +79 -49
  3. data/TUTORIAL.md +9 -20
  4. data/ext/htslib_native/extconf.rb +41 -0
  5. data/ext/htslib_native/htslib_native.h +11 -0
  6. data/ext/htslib_native/htslib_native_ext.c +48 -0
  7. data/ext/htslib_native/native_bam.c +1108 -0
  8. data/ext/htslib_native/native_bcf.c +369 -0
  9. data/ext/htslib_native/native_faidx.c +155 -0
  10. data/ext/htslib_native/native_tabix.c +246 -0
  11. data/lib/hts/bam/auxi.rb +87 -342
  12. data/lib/hts/bam/base_mod.rb +37 -73
  13. data/lib/hts/bam/cigar.rb +9 -55
  14. data/lib/hts/bam/flag.rb +19 -27
  15. data/lib/hts/bam/header.rb +29 -56
  16. data/lib/hts/bam/mpileup.rb +158 -132
  17. data/lib/hts/bam/pileup.rb +120 -145
  18. data/lib/hts/bam/record.rb +233 -270
  19. data/lib/hts/bam.rb +102 -42
  20. data/lib/hts/bcf/errors.rb +1 -0
  21. data/lib/hts/bcf/format.rb +278 -379
  22. data/lib/hts/bcf/header.rb +38 -123
  23. data/lib/hts/bcf/header_record.rb +9 -35
  24. data/lib/hts/bcf/info.rb +72 -320
  25. data/lib/hts/bcf/record.rb +61 -102
  26. data/lib/hts/bcf.rb +149 -323
  27. data/lib/hts/faidx.rb +28 -87
  28. data/lib/hts/hts.rb +6 -94
  29. data/lib/hts/native.rb +13 -0
  30. data/lib/hts/tabix.rb +93 -48
  31. data/lib/hts/version.rb +1 -1
  32. data/lib/htslib.rb +6 -52
  33. metadata +13 -64
  34. data/lib/hts/ffi_ext/README.md +0 -8
  35. data/lib/hts/ffi_ext/pointer.rb +0 -18
  36. data/lib/hts/ffi_ext/struct.rb +0 -45
  37. data/lib/hts/libhts/bgzf.rb +0 -199
  38. data/lib/hts/libhts/constants.rb +0 -658
  39. data/lib/hts/libhts/cram.rb +0 -471
  40. data/lib/hts/libhts/fai.rb +0 -146
  41. data/lib/hts/libhts/hfile.rb +0 -109
  42. data/lib/hts/libhts/hts.rb +0 -477
  43. data/lib/hts/libhts/kfunc.rb +0 -38
  44. data/lib/hts/libhts/sam.rb +0 -760
  45. data/lib/hts/libhts/sam_funcs.rb +0 -155
  46. data/lib/hts/libhts/tbx.rb +0 -79
  47. data/lib/hts/libhts/tbx_funcs.rb +0 -32
  48. data/lib/hts/libhts/thread_pool.rb +0 -139
  49. data/lib/hts/libhts/vcf.rb +0 -557
  50. data/lib/hts/libhts/vcf_funcs.rb +0 -353
  51. data/lib/hts/libhts.rb +0 -47
data/lib/hts/faidx.rb CHANGED
@@ -1,21 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../htslib"
4
-
5
- module HTS
6
- module LibC
7
- extend FFI::Library
8
- ffi_lib FFI::Library::LIBC
9
- attach_function :free, [:pointer], :void
10
- end
11
- end
3
+ require_relative "native"
12
4
 
13
5
  module HTS
14
6
  class Faidx
15
7
  attr_reader :file_name, :format
16
8
 
17
9
  def self.open(file_name, format: :auto, auto_build: true)
18
- file = new(file_name, format:, auto_build:) # do not yield
10
+ file = new(file_name, format:, auto_build:)
19
11
  return file unless block_given?
20
12
 
21
13
  begin
@@ -27,10 +19,9 @@ module HTS
27
19
  end
28
20
 
29
21
  def self.build_index(file_name, fai_path = nil, gzi_path = nil)
30
- case LibHTS.fai_build3(file_name, fai_path, gzi_path)
31
- when 0
32
- else raise HTS::Error, "Failed to build faidx index for #{file_name}"
33
- end
22
+ return if Native::FaidxHandle.build(file_name, fai_path, gzi_path).zero?
23
+
24
+ raise HTS::Error, "Failed to build faidx index for #{file_name}"
34
25
  end
35
26
 
36
27
  def initialize(file_name, format: :auto, auto_build: true)
@@ -38,95 +29,62 @@ module HTS
38
29
 
39
30
  @file_name = file_name.freeze
40
31
  @format = resolve_format(@file_name, format)
41
- @fai = load_handle(@file_name, @format, auto_build)
42
-
43
- raise Errno::ENOENT, "Failed to open #{@file_name}" if @fai.null?
44
- end
45
-
46
- def struct
47
- @fai
48
- end
49
-
50
- def close
51
- return if closed?
52
-
53
- LibHTS.fai_destroy(@fai)
54
- @fai = nil
55
- end
56
-
57
- def closed?
58
- @fai.nil? || @fai.null?
59
- end
60
-
61
- def size
62
- check_closed
63
- LibHTS.faidx_nseq(@fai)
32
+ @native = Native::FaidxHandle.open(@file_name, @format == :fastq ? 1 : 0, auto_build)
33
+ rescue Errno::ENOENT
34
+ raise Errno::ENOENT, "Failed to open #{@file_name}"
64
35
  end
65
36
 
37
+ def close = @native&.close
38
+ def closed? = @native.nil? || @native.closed?
39
+ def size = native.size
66
40
  alias length size
67
-
68
- def names
69
- check_closed
70
- Array.new(length) { |i| LibHTS.faidx_iseq(@fai, i) }
71
- end
41
+ def names = native.names
72
42
 
73
43
  def has_seq?(key)
74
- check_closed
75
44
  raise ArgumentError, "Expect chrom to be String or Symbol" unless key.is_a?(String) || key.is_a?(Symbol)
76
45
 
77
- key = key.to_s
78
- case LibHTS.faidx_has_seq(@fai, key)
79
- when 1 then true
80
- when 0 then false
81
- else raise HTS::Error, "Unexpected return value from faidx_has_seq"
82
- end
46
+ native.has_seq?(key.to_s)
83
47
  end
84
48
 
85
49
  def seq_len(chrom)
86
- check_closed
87
50
  raise ArgumentError, "Expect chrom to be String or Symbol" unless chrom.is_a?(String) || chrom.is_a?(Symbol)
88
51
 
89
52
  chrom = chrom.to_s
90
- result = LibHTS.faidx_seq_len64(@fai, chrom)
53
+ result = native.seq_len(chrom)
91
54
  raise ArgumentError, "Sequence not found: #{chrom}" if result == -1
92
55
 
93
56
  result
94
57
  end
95
58
 
96
59
  def fetch_seq(name, start = nil, stop = nil)
97
- check_closed
98
60
  name = name.to_s
99
-
100
61
  if start.nil? && stop.nil?
101
62
  len = seq_len(name)
102
63
  return "" if len.zero?
103
64
 
104
- fetch_seq(name, 0, len - 1)
65
+ start = 0
66
+ stop = len - 1
105
67
  else
106
68
  validate_range!(name, start, stop)
107
- rlen = FFI::MemoryPointer.new(:int64)
108
- result = LibHTS.faidx_fetch_seq64(@fai, name, start, stop, rlen)
109
- fetch_result(result, rlen.read_int64, "sequence", name, start, stop)
110
69
  end
70
+ fetch_result(native.fetch_seq(name, start, stop), "sequence", name, start, stop)
111
71
  end
112
72
 
113
73
  def fetch_qual(name, start = nil, stop = nil)
114
- check_closed
74
+ native
115
75
  raise HTS::Error, "Quality is only available for FASTQ indexes" unless format == :fastq
116
76
 
117
77
  name = name.to_s
118
-
119
78
  if start.nil? && stop.nil?
120
79
  len = seq_len(name)
121
80
  return "" if len.zero?
122
81
 
123
- fetch_qual(name, 0, len - 1)
82
+ start = 0
83
+ stop = len - 1
124
84
  else
125
85
  validate_range!(name, start, stop)
126
- rlen = FFI::MemoryPointer.new(:int64)
127
- result = LibHTS.faidx_fetch_qual64(@fai, name, start, stop, rlen)
128
- fetch_result(result, rlen.read_int64, "quality", name, start, stop)
129
86
  end
87
+ fetch_result(native.fetch_qual(name, start, stop), "quality", name, start, stop)
130
88
  end
131
89
 
132
90
  def build_index(fai_path = nil, gzi_path = nil)
@@ -136,8 +94,10 @@ module HTS
136
94
 
137
95
  private
138
96
 
139
- def check_closed
97
+ def native
140
98
  raise IOError, "closed Faidx" if closed?
99
+
100
+ @native
141
101
  end
142
102
 
143
103
  def validate_range!(name, start, stop)
@@ -149,34 +109,15 @@ module HTS
149
109
  raise ArgumentError, "Expect stop to be < seq_len (#{len})" if stop >= len
150
110
  end
151
111
 
152
- def fetch_result(ptr, len, kind, name, start, stop)
112
+ def fetch_result(result, kind, name, start, stop)
113
+ len, string = result
153
114
  case len
154
115
  when -2 then raise ArgumentError, "Sequence not found: #{name}"
155
116
  when -1 then raise HTS::Error, "Error fetching #{kind}: #{name}:#{start}-#{stop}"
156
117
  end
118
+ raise HTS::Error, "Error fetching #{kind}: #{name}:#{start}-#{stop}" unless string
157
119
 
158
- raise HTS::Error, "Error fetching #{kind}: #{name}:#{start}-#{stop}" if ptr.null?
159
-
160
- begin
161
- ptr.read_string_length(len)
162
- ensure
163
- HTS::LibC.free(ptr)
164
- end
165
- end
166
-
167
- def load_handle(file_name, format, auto_build)
168
- case [format, auto_build]
169
- when [:fasta, true]
170
- LibHTS.fai_load_format(file_name, :FAI_FASTA)
171
- when [:fastq, true]
172
- LibHTS.fai_load_format(file_name, :FAI_FASTQ)
173
- when [:fasta, false]
174
- LibHTS.fai_load3_format(file_name, nil, nil, 0, :FAI_FASTA)
175
- when [:fastq, false]
176
- LibHTS.fai_load3_format(file_name, nil, nil, 0, :FAI_FASTQ)
177
- else
178
- raise ArgumentError, "Unsupported format: #{format}"
179
- end
120
+ string
180
121
  end
181
122
 
182
123
  def resolve_format(file_name, format)
data/lib/hts/hts.rb CHANGED
@@ -3,7 +3,8 @@
3
3
  require_relative "../htslib"
4
4
 
5
5
  module HTS
6
- # A base class for hts files.
6
+ # Shared behavior for HTS-backed file classes. Native handles remain private
7
+ # implementation details of the concrete classes.
7
8
  class Hts
8
9
  class << self
9
10
  private
@@ -12,10 +13,11 @@ module HTS
12
13
  define_method(name) do
13
14
  check_closed
14
15
  position = tell
15
- ary = map(&name)
16
+ values = map(&name)
16
17
  seek(position) if position
17
- ary
18
+ values
18
19
  end
20
+ alias_method "#{name}_array", name
19
21
  end
20
22
 
21
23
  def define_iterator(name)
@@ -23,9 +25,7 @@ module HTS
23
25
  check_closed
24
26
  return to_enum(__method__) unless block
25
27
 
26
- each do |record|
27
- block.call(record.public_send(name))
28
- end
28
+ each { |record| block.call(record.public_send(name)) }
29
29
  self
30
30
  end
31
31
  end
@@ -35,94 +35,6 @@ module HTS
35
35
  raise TypeError, "Can't make instance of HTS abstract class"
36
36
  end
37
37
 
38
- def struct
39
- @hts_file
40
- end
41
-
42
- def to_ptr
43
- @hts_file.to_ptr
44
- end
45
-
46
- def file_format
47
- LibHTS.hts_get_format(@hts_file)[:format].to_s
48
- end
49
-
50
- def file_format_version
51
- v = LibHTS.hts_get_format(@hts_file)[:version]
52
- major = v[:major]
53
- minor = v[:minor]
54
- if minor == -1
55
- major.to_s
56
- else
57
- "#{major}.#{minor}"
58
- end
59
- end
60
-
61
- def close
62
- return if closed?
63
-
64
- LibHTS.hts_close(@hts_file)
65
- @hts_file = nil
66
- end
67
-
68
- def closed?
69
- @hts_file.nil? || @hts_file.null?
70
- end
71
-
72
- def fai=(fai)
73
- check_closed
74
- r = LibHTS.hts_set_fai_filename(@hts_file, fai)
75
- raise "Failed to load fasta index: #{fai}" if r.negative?
76
-
77
- self
78
- end
79
-
80
- def set_threads(n = nil)
81
- if n.nil?
82
- require "etc"
83
- n = [Etc.nprocessors - 1, 1].max
84
- end
85
- raise TypeError unless n.is_a?(Integer)
86
- raise ArgumentError, "Number of threads must be positive" if n < 1
87
-
88
- r = LibHTS.hts_set_threads(@hts_file, n)
89
- raise "Failed to set number of threads: #{threads}" if r < 0
90
-
91
- @nthreads = n
92
- self
93
- end
94
-
95
- def seek(offset)
96
- if @hts_file[:is_cram] == 1
97
- LibHTS.cram_seek(@hts_file[:fp][:cram], offset, IO::SEEK_SET)
98
- elsif @hts_file[:is_bgzf] == 1
99
- LibHTS.bgzf_seek(@hts_file[:fp][:bgzf], offset, IO::SEEK_SET)
100
- else
101
- LibHTS.hseek(@hts_file[:fp][:hfile], offset, IO::SEEK_SET)
102
- end
103
- end
104
-
105
- def tell
106
- if @hts_file[:is_cram] == 1
107
- # LibHTS.cram_tell(@hts_file[:fp][:cram])
108
- # warn 'cram_tell is not implemented in c htslib'
109
- nil
110
- elsif @hts_file[:is_bgzf] == 1
111
- LibHTS.bgzf_tell(@hts_file[:fp][:bgzf])
112
- else
113
- LibHTS.htell(@hts_file[:fp][:hfile])
114
- end
115
- end
116
-
117
- def rewind
118
- raise "Cannot rewind: no start position" unless @start_position
119
-
120
- r = seek(@start_position)
121
- raise "Failed to rewind: #{r}" if r < 0
122
-
123
- tell
124
- end
125
-
126
38
  private
127
39
 
128
40
  def check_closed
data/lib/hts/native.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "htslib_native_ext"
5
+ rescue LoadError => e
6
+ raise LoadError, <<~MESSAGE
7
+ #{e.message}
8
+ ruby-htslib requires its native extension linked against HTSlib.
9
+ Install the HTSlib development package and reinstall the gem. If HTSlib is
10
+ in a non-standard prefix, use --with-htslib-dir or set HTSLIBDIR while
11
+ building the gem.
12
+ MESSAGE
13
+ end
data/lib/hts/tabix.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative "../htslib"
4
4
 
5
5
  require_relative "hts"
6
+ require_relative "native"
6
7
 
7
8
  module HTS
8
9
  class Tabix < Hts
@@ -34,14 +35,13 @@ module HTS
34
35
  @index_name = index
35
36
  @mode = "r"
36
37
  @nthreads = threads
37
- @hts_file = LibHTS.hts_open(@file_name, @mode)
38
-
39
- raise Errno::ENOENT, "Failed to open #{@file_name}" if @hts_file.null?
38
+ @index_load_attempted = false
39
+ @native = Native::TabixHandle.open(@file_name)
40
40
 
41
41
  set_threads(threads) if threads
42
42
 
43
43
  # build_index(index) if build_index
44
- @idx = load_index(index)
44
+ load_index(index) if index
45
45
  end
46
46
 
47
47
  def build_index(index_name = nil, min_shift: 0)
@@ -49,7 +49,7 @@ module HTS
49
49
 
50
50
  if index_name
51
51
  warn "Create index for #{@file_name} to #{index_name}"
52
- case LibHTS.tbx_index_build2(@file_name, index_name, min_shift, LibHTS.tbx_conf_vcf)
52
+ case Native::TabixHandle.build(@file_name, index_name, min_shift)
53
53
  when 0 # successful
54
54
  when -1 then raise "general failure"
55
55
  when -2 then raise "compression not BGZF"
@@ -57,104 +57,149 @@ module HTS
57
57
  end
58
58
  else
59
59
  warn "Create index for #{@file_name}"
60
- case LibHTS.tbx_index_build(@file_name, min_shift, LibHTS.tbx_conf_vcf)
60
+ case Native::TabixHandle.build(@file_name, nil, min_shift)
61
61
  when 0 # successful
62
62
  when -1 then raise "general failure"
63
63
  when -2 then raise "compression not BGZF"
64
64
  else raise "unknown error"
65
65
  end
66
66
  end
67
+ @index_name = index_name
68
+ @index_load_attempted = false
67
69
  self # for method chaining
68
70
  end
69
71
 
70
72
  def load_index(index_name = nil)
71
73
  check_closed
72
- if index_name
73
- LibHTS.tbx_index_load2(@file_name, index_name)
74
- else
75
- LibHTS.tbx_index_load3(@file_name, nil, 2)
76
- end
74
+ @index_name = index_name
75
+ @index_load_attempted = true
76
+ @native.load_index(index_name)
77
77
  end
78
78
 
79
79
  def index_loaded?
80
80
  check_closed
81
- !@idx.null?
81
+ @native.index_loaded?
82
82
  end
83
83
 
84
84
  def name2id(name)
85
85
  check_closed
86
- LibHTS.tbx_name2id(@idx, name)
86
+ raise "Index file is required to call the name2id method." unless ensure_index_loaded
87
+
88
+ @native.name2id(name)
87
89
  end
88
90
 
89
91
  def seqnames
90
92
  check_closed
91
- nseq = FFI::MemoryPointer.new(:int)
92
- pts = LibHTS.tbx_seqnames(@idx, nseq)
93
- begin
94
- pts.read_array_of_pointer(nseq.read_int).map(&:read_string)
95
- ensure
96
- LibHTS.hts_free(pts) unless pts.null?
97
- end
93
+ raise "Index file is required to call the seqnames method." unless ensure_index_loaded
94
+
95
+ @native.seqnames
98
96
  end
99
97
 
100
98
  def query(region, start = nil, end_ = nil, &block)
99
+ query_with_mode(region, start, end_, :fields, nil, &block)
100
+ end
101
+
102
+ # Explicit name for the legacy split-fields API.
103
+ def each_fields(region, start = nil, end_ = nil, &block)
104
+ query_with_mode(region, start, end_, :fields, nil, &block)
105
+ end
106
+
107
+ # Iterate raw rows, allocating one String per row and no field Array.
108
+ def each_line(region, start = nil, end_ = nil, &block)
109
+ query_with_mode(region, start, end_, :line, nil, &block)
110
+ end
111
+
112
+ # Extract only requested zero-based columns without materializing unused
113
+ # field strings. This Ruby implementation scans tab delimiters once.
114
+ def each_selected_fields(region, *columns, &block)
115
+ raise ArgumentError, "at least one column index is required" if columns.empty?
116
+
117
+ normalized = columns.map do |column|
118
+ index = Integer(column)
119
+ raise ArgumentError, "column indices must be non-negative" if index.negative?
120
+
121
+ index
122
+ end
123
+ query_with_mode(region, nil, nil, :selected, normalized, &block)
124
+ end
125
+
126
+ def query_with_mode(region, start, end_, mode, columns, &block)
101
127
  check_closed
102
- raise "Index file is required to call the query method." unless index_loaded?
128
+ raise "Index file is required to call the query method." unless ensure_index_loaded
103
129
 
104
130
  if start && end_
105
- queryi(name2id(region), start, end_, &block)
131
+ queryi(name2id(region), start, end_, mode, columns, &block)
132
+ elsif start.nil? && end_.nil?
133
+ querys(region, mode, columns, &block)
106
134
  else
107
- querys(region, &block)
135
+ raise ArgumentError, "start and end must be specified together"
108
136
  end
109
137
  end
110
138
 
111
139
  def close
112
140
  return if closed?
113
141
 
114
- @idx.close if @idx && !@idx.null?
115
- @idx = nil
116
- super
142
+ @native.close
117
143
  end
118
144
 
119
145
  def closed?
120
- @hts_file.nil? || @hts_file.null?
146
+ @native.nil? || @native.closed?
147
+ end
148
+
149
+ def file_format = @native.file_format
150
+ def file_format_version = @native.file_format_version
151
+
152
+ def set_threads(n = nil)
153
+ if n.nil?
154
+ require "etc"
155
+ n = [Etc.nprocessors - 1, 1].max
156
+ end
157
+ raise TypeError unless n.is_a?(Integer)
158
+ raise ArgumentError, "Number of threads must be positive" if n < 1
159
+ raise "Failed to set number of threads: #{n}" if @native.set_threads(n).negative?
160
+
161
+ @nthreads = n
162
+ self
121
163
  end
122
164
 
123
165
  private
124
166
 
125
- def queryi(id, start, end_, &block)
126
- return to_enum(__method__, id, start, end_) unless block_given?
167
+ def ensure_index_loaded
168
+ return true if index_loaded?
169
+ return false if @index_load_attempted
127
170
 
128
- qiter = LibHTS.tbx_itr_queryi(@idx, id, start, end_)
129
- raise "Failed to query region: #{id}:#{start}-#{end_}" if qiter.null?
171
+ load_index(@index_name)
172
+ end
130
173
 
131
- query_yield(qiter, &block)
174
+ def queryi(id, start, end_, mode = :fields, columns = nil, &block)
175
+ return to_enum(__method__, id, start, end_, mode, columns) unless block_given?
176
+
177
+ @native.query_interval(id, start, end_) { |line| yield_line(line, mode, columns, &block) }
132
178
  self
133
179
  end
134
180
 
135
- def querys(region, &block)
136
- return to_enum(__method__, region) unless block_given?
137
-
138
- qiter = LibHTS.tbx_itr_querys(@idx, region)
139
- raise "Failed to query region: #{region}" if qiter.null?
181
+ def querys(region, mode = :fields, columns = nil, &block)
182
+ return to_enum(__method__, region, mode, columns) unless block_given?
140
183
 
141
- query_yield(qiter, &block)
184
+ @native.query_region(region) { |line| yield_line(line, mode, columns, &block) }
142
185
  self
143
186
  end
144
187
 
145
- def query_yield(qiter)
146
- r = LibHTS::KString.new
147
- begin
148
- while (slen = LibHTS.tbx_itr_next(@hts_file, @idx, qiter, r)) >= 0
149
- yield r.read_string_copy.split("\t")
150
- end
151
- raise if slen < -1
152
- ensure
153
- r.free_buffer
154
- LibHTS.hts_itr_destroy(qiter)
188
+ def yield_line(line, mode, columns)
189
+ case mode
190
+ when :line
191
+ yield line
192
+ when :selected
193
+ yield selected_fields(line, columns)
194
+ else
195
+ yield line.split("\t")
155
196
  end
156
197
  end
157
198
 
199
+ def selected_fields(line, columns)
200
+ HTS::Native.selected_fields(line, columns)
201
+ end
202
+
158
203
  def check_closed
159
204
  raise IOError, "closed Tabix" if closed?
160
205
  end
data/lib/hts/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTS
4
- VERSION = "0.4.1"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/htslib.rb CHANGED
@@ -1,60 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ffi"
4
-
5
3
  require "hts/version"
4
+ require "hts/native"
6
5
 
7
6
  module HTS
8
7
  class Error < StandardError; end
9
8
 
10
- class << self
11
- attr_accessor :lib_path
12
-
13
- def search_htslib(name = nil)
14
- name ||= "libhts.#{FFI::Platform::LIBSUFFIX}"
15
- lib_path = if ENV["HTSLIBDIR"]
16
- File.expand_path(name, ENV["HTSLIBDIR"])
17
- else
18
- File.expand_path("../vendor/#{name}", __dir__)
19
- end
20
- return lib_path if File.exist?(lib_path)
21
-
22
- begin
23
- require "pkg-config"
24
- lib_dir = PKGConfig.variable("htslib", "libdir")
25
- lib_path = File.expand_path(name, lib_dir)
26
- rescue PackageConfig::NotFoundError
27
- warn "htslib.pc was not found in the pkg-config search path."
28
- end
29
- return lib_path if File.exist?(lib_path)
30
-
31
- warn "htslib shared library '#{name}' not found."
32
- end
33
-
34
- def search_htslib_windows
35
- ENV["HTSLIBDIR"] ||= [
36
- RubyInstaller::Runtime.msys2_installation.msys_path,
37
- RubyInstaller::Runtime.msys2_installation.mingwarch
38
- ].join(File::ALT_SEPARATOR)
39
- path = File.expand_path("bin/hts-3.dll", ENV["HTSLIBDIR"])
40
- RubyInstaller::Runtime.add_dll_directory(File.dirname(path))
41
- path
42
- end
43
- end
44
-
45
- self.lib_path = if Object.const_defined?(:RubyInstaller)
46
- search_htslib_windows
47
- else
48
- search_htslib
49
- end
50
-
51
- # You can change the path of the shared library with `HTS.lib_path=`
52
- # before calling the LibHTS module.
53
- autoload :LibHTS, "hts/libhts"
54
-
55
- autoload :Hts, "hts/hts"
56
- autoload :Bam, "hts/bam"
57
- autoload :Bcf, "hts/bcf"
58
- autoload :Faidx, "hts/faidx"
59
- autoload :Tabix, "hts/tabix"
9
+ autoload :Hts, "hts/hts"
10
+ autoload :Bam, "hts/bam"
11
+ autoload :Bcf, "hts/bcf"
12
+ autoload :Faidx, "hts/faidx"
13
+ autoload :Tabix, "hts/tabix"
60
14
  end