summaryrefslogtreecommitdiff
path: root/ext/dbm/extconf.rb
blob: 2c8caa398ebf10d04f459b3b3adc426b8abc286f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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
141
142
143
144
145
146
147
148
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
182
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# configure option:
#   --with-dbm-type=COMMA-SEPARATED-NDBM-TYPES
#
# ndbm type:
#   libc        ndbm compatible library in libc.
#   db          Berkeley DB (libdb)
#   db2         Berkeley DB (libdb2)
#   db1         Berkeley DB (libdb1)
#   db5         Berkeley DB (libdb5)
#   db4         Berkeley DB (libdb4)
#   db3         Berkeley DB (libdb3)
#   gdbm_compat GDBM since 1.8.1 (libgdbm_compat)
#   gdbm        GDBM until 1.8.0 (libgdbm)
#   qdbm        QDBM (libqdbm)
#   ndbm        Some legacy OS may have libndbm.

# :stopdoc:
require 'mkmf'

dir_config("dbm")

if dblib = with_config("dbm-type", nil)
  dblib = dblib.split(/[ ,]+/)
else
  dblib = %w(libc db db2 db1 db5 db4 db3 gdbm_compat gdbm qdbm)
end

headers = {
  "libc" => ["ndbm.h"], # 4.3BSD original ndbm, Berkeley DB 1 in 4.4BSD libc.
  "db" => ["db.h"],
  "db1" => ["db1/ndbm.h", "db1.h", "ndbm.h"],
  "db2" => ["db2/db.h", "db2.h", "db.h"],
  "db3" => ["db3/db.h", "db3.h", "db.h"],
  "db4" => ["db4/db.h", "db4.h", "db.h"],
  "db5" => ["db5/db.h", "db5.h", "db.h"],
  "gdbm_compat" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM since 1.8.1
  "gdbm" => ["gdbm-ndbm.h", "gdbm/ndbm.h", "ndbm.h"], # GDBM until 1.8.0
  "qdbm" => ["qdbm/relic.h", "relic.h"],
}

class << headers
  attr_accessor :found
  attr_accessor :defs
end
headers.found = []
headers.defs = nil

def headers.db_check(db, hdr)
  old_libs = $libs.dup
  old_defs = $defs.dup
  result = db_check2(db, hdr)
  if !result
    $libs = old_libs
    $defs = old_defs
  end
  result
end

def have_declared_libvar(var, headers = nil, opt = "", &b)
  checking_for checking_message([*var].compact.join(' '), headers, opt) do
    try_declared_libvar(var, headers, opt, &b)
  end
end

def try_declared_libvar(var, headers = nil, opt = "", &b)
  if try_link(<<"SRC", opt, &b)
#{cpp_include(headers)}
/*top*/
int main(int argc, char *argv[]) {
  void *conftest_var = &#{var};
  return 0;
}
SRC
    $defs.push(format("-DHAVE_DECLARED_LIBVAR_%s", var.tr_cpp))
    true
  else
    false
  end
end

def have_undeclared_libvar(var, headers = nil, opt = "", &b)
  checking_for checking_message([*var].compact.join(' '), headers, opt) do
    try_undeclared_libvar(var, headers, opt, &b)
  end
end

def try_undeclared_libvar(var, headers = nil, opt = "", &b)
  var, type = *var
  if try_link(<<"SRC", opt, &b)
#{cpp_include(headers)}
/*top*/
int main(int argc, char *argv[]) {
  typedef #{type || 'int'} conftest_type;
  extern conftest_type #{var};
  conftest_type *conftest_var = &#{var};
  return 0;
}
SRC
    $defs.push(format("-DHAVE_UNDECLARED_LIBVAR_%s", var.tr_cpp))
    true
  else
    false
  end
end

def have_empty_macro_dbm_clearerr(headers = nil, opt = "", &b)
  checking_for checking_message('empty macro of dbm_clearerr(foobarbaz)',
                                headers, opt) do
    try_toplevel('dbm_clearerr(foobarbaz)', headers, opt, &b)
  end
end

def try_toplevel(src, headers = nil, opt = "", &b)
  if try_compile(<<"SRC", opt, &b)
#{cpp_include(headers)}
/*top*/
#{src}
SRC
    true
  else
    false
  end
end


def headers.db_check2(db, hdr)
  $defs.push(%{-DRUBYDBM_DBM_HEADER='"#{hdr}"'})
  $defs.push(%{-DRUBYDBM_DBM_TYPE='"#{db}"'})

  hsearch = nil

  case db
  when /^db[2-5]?$/
    hsearch = "-DDB_DBM_HSEARCH"
  when "gdbm_compat"
    have_library("gdbm") or return false
  end

  if !have_type("DBM", hdr, hsearch)
    return false
  end

  # 'libc' means ndbm is provided by libc.
  # 4.3BSD original ndbm is contained in libc.
  # 4.4BSD (and its derivatives such as NetBSD) contains Berkeley DB 1 in libc.
  if !(db == 'libc' ? have_func('dbm_open("", 0, 0)', hdr, hsearch) :
                      have_library(db, 'dbm_open("", 0, 0)', hdr, hsearch))
    return false
  end

  # Skip a mismatch of Berkeley DB's ndbm.h and old GDBM library.
  #
  # dbm_clearerr() should be available for any ndbm implementation.
  # It is available since the original (4.3BSD) ndbm and standardized by POSIX.
  #
  # However "can't resolve symbol 'dbm_clearerr'" problem may be caused by
  # header/library mismatch: Berkeley DB ndbm.h and GDBM library until 1.8.3.
  # GDBM (until 1.8.3) provides dbm_clearerr() as a empty macro in the header
  # and the library don't provide dbm_clearerr().
  # Berkeley DB provides dbm_clearerr() as a usual function.
  # So Berkeley DB header with GDBM library causes the problem.
  #
  if !have_func('dbm_clearerr((DBM *)0)', hdr, hsearch)
    return false
  end

  # Berkeley DB's ndbm.h (since 1.85 at least) defines DBM_SUFFIX.
  # Note that _DB_H_ is not defined on Mac OS X because
  # it uses Berkeley DB 1 but ndbm.h doesn't include db.h.
  have_db_header = have_macro('DBM_SUFFIX', hdr, hsearch)

  # Old GDBM's ndbm.h, until 1.8.3, defines dbm_clearerr as a macro which
  # expands to no tokens.
  have_gdbm_header1 = have_empty_macro_dbm_clearerr(hdr, hsearch)

  # Recent GDBM's ndbm.h, since 1.9, includes gdbm.h and it defines _GDBM_H_.
  # ndbm compatibility layer of GDBM is provided by libgdbm (until 1.8.0)
  # and libgdbm_compat (since 1.8.1).
  have_gdbm_header2 = have_macro('_GDBM_H_', hdr, hsearch)

  # 4.3BSD's ndbm.h defines _DBM_IOERR.
  # The original ndbm is provided by libc in 4.3BSD.
  have_ndbm_header = have_macro('_DBM_IOERR', hdr, hsearch)

  # GDBM provides ndbm functions in libgdbm_compat since GDBM 1.8.1.
  # GDBM's ndbm.h defines _GDBM_H_ since GDBM 1.9.
  # If _GDBM_H_ is defined, 'gdbm_compat' is required and reject 'gdbm'.
  if have_gdbm_header2 && db == 'gdbm'
    return false
  end

  if have_db_header
    $defs.push('-DRUBYDBM_DB_HEADER')
  end

  have_gdbm_header = have_gdbm_header1 | have_gdbm_header2
  if have_gdbm_header
    $defs.push('-DRUBYDBM_GDBM_HEADER')
  end

  # ndbm.h is provided by the original (4.3BSD) ndbm,
  # Berkeley DB 1 in libc of 4.4BSD and
  # ndbm compatibility layer of GDBM.
  # So, try to check header/library mismatch.
  #
  # Several (possibly historical) distributions provides libndbm.
  # It may be Berkeley DB, GDBM or 4.3BSD ndbm.
  # So mismatch check is not performed for that.
  # Note that libndbm is searched only when --with-dbm-type=ndbm is
  # given for configure.
  #
  if hdr == 'ndbm.h' && db != 'libc' && db != 'ndbm'
    if /\Adb\d?\z/ !~ db && have_db_header
      return false
    end

    if /\Agdbm/ !~ db && have_gdbm_header
      return false
    end

    if have_ndbm_header
      return false
    end
  end

  # Berkeley DB
  have_func('db_version((int *)0, (int *)0, (int *)0)', hdr, hsearch)

  # GDBM
  have_gdbm_version = have_declared_libvar("gdbm_version", hdr, hsearch)
  # gdbm_version is available since very old version (GDBM 1.5 at least).
  # However it is not declared by ndbm.h until GDBM 1.8.3.
  # We can't include both ndbm.h and gdbm.h because they both define datum type.
  # ndbm.h includes gdbm.h and gdbm_version is declared since GDBM 1.9.
  have_gdbm_version |= have_undeclared_libvar(["gdbm_version", "char *"], hdr, hsearch)

  # QDBM
  have_var("dpversion", hdr, hsearch)

  # detect mismatch between GDBM header and other library.
  # If GDBM header is included, GDBM library should be linked.
  if have_gdbm_header && !have_gdbm_version
    return false
  end

  # DBC type is required to disable error messages by Berkeley DB 2 or later.
  if have_db_header
    have_type("DBC", hdr, hsearch)
  end

  if hsearch
    $defs << hsearch
    @defs = hsearch
  end
  $defs << '-DDBM_HDR="<'+hdr+'>"'
  @found << hdr

  puts "header: #{hdr}"
  puts "library: #{db}"

  true
end

if dblib.any? {|db| headers.fetch(db, ["ndbm.h"]).any? {|hdr| headers.db_check(db, hdr) } }
  have_header("cdefs.h")
  have_header("sys/cdefs.h")
  have_func("dbm_pagfno((DBM *)0)", headers.found, headers.defs)
  have_func("dbm_dirfno((DBM *)0)", headers.found, headers.defs)
  convertible_int("datum.dsize", headers.found, headers.defs)
  checking_for("sizeof(DBM) is available") {
    if try_compile(<<SRC)
#ifdef HAVE_CDEFS_H
# include <cdefs.h>
#endif
#ifdef HAVE_SYS_CDEFS_H
# include <sys/cdefs.h>
#endif
#include DBM_HDR

const int sizeof_DBM = (int)sizeof(DBM);
SRC
      $defs << '-DDBM_SIZEOF_DBM=sizeof(DBM)'
    else
      $defs << '-DDBM_SIZEOF_DBM=0'
    end
  }
  create_makefile("dbm")
end
# :startdoc: