ruby.py 5.4 KB
Newer Older
XuanDai's avatar
XuanDai 已提交
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
#!/usr/bin/env python
# encoding: utf-8
# daniel.svensson at purplescout.se 2008
# Thomas Nagy 2016-2018 (ita)

"""
Support for Ruby extensions. A C/C++ compiler is required::

	def options(opt):
		opt.load('compiler_c ruby')
	def configure(conf):
		conf.load('compiler_c ruby')
		conf.check_ruby_version((1,8,0))
		conf.check_ruby_ext_devel()
		conf.check_ruby_module('libxml')
	def build(bld):
		bld(
			features = 'c cshlib rubyext',
			source = 'rb_mytest.c',
			target = 'mytest_ext',
			install_path = '${ARCHDIR_RUBY}')
		bld.install_files('${LIBDIR_RUBY}', 'Mytest.rb')
"""

import os
from waflib import Errors, Options, Task, Utils
from waflib.TaskGen import before_method, feature, extension
from waflib.Configure import conf

@feature('rubyext')
@before_method('apply_incpaths', 'process_source', 'apply_bundle', 'apply_link')
def init_rubyext(self):
	"""
	Add required variables for ruby extensions
	"""
	self.install_path = '${ARCHDIR_RUBY}'
	self.uselib = self.to_list(getattr(self, 'uselib', ''))
	if not 'RUBY' in self.uselib:
		self.uselib.append('RUBY')
	if not 'RUBYEXT' in self.uselib:
		self.uselib.append('RUBYEXT')

@feature('rubyext')
@before_method('apply_link', 'propagate_uselib_vars')
def apply_ruby_so_name(self):
	"""
	Strip the *lib* prefix from ruby extensions
	"""
	self.env.cshlib_PATTERN = self.env.cxxshlib_PATTERN = self.env.rubyext_PATTERN

@conf
def check_ruby_version(self, minver=()):
	"""
	Checks if ruby is installed.
	If installed the variable RUBY will be set in environment.
	The ruby binary can be overridden by ``--with-ruby-binary`` command-line option.
	"""

	ruby = self.find_program('ruby', var='RUBY', value=Options.options.rubybinary)

	try:
		version = self.cmd_and_log(ruby + ['-e', 'puts defined?(VERSION) ? VERSION : RUBY_VERSION']).strip()
	except Errors.WafError:
		self.fatal('could not determine ruby version')
	self.env.RUBY_VERSION = version

	try:
		ver = tuple(map(int, version.split('.')))
	except Errors.WafError:
		self.fatal('unsupported ruby version %r' % version)

	cver = ''
	if minver:
		cver = '> ' + '.'.join(str(x) for x in minver)
		if ver < minver:
			self.fatal('ruby is too old %r' % ver)

	self.msg('Checking for ruby version %s' % cver, version)

@conf
def check_ruby_ext_devel(self):
	"""
	Check if a ruby extension can be created
	"""
	if not self.env.RUBY:
		self.fatal('ruby detection is required first')

	if not self.env.CC_NAME and not self.env.CXX_NAME:
		self.fatal('load a c/c++ compiler first')

	version = tuple(map(int, self.env.RUBY_VERSION.split(".")))

	def read_out(cmd):
		return Utils.to_list(self.cmd_and_log(self.env.RUBY + ['-rrbconfig', '-e', cmd]))

	def read_config(key):
		return read_out('puts RbConfig::CONFIG[%r]' % key)

	cpppath = archdir = read_config('archdir')

	if version >= (1, 9, 0):
		ruby_hdrdir = read_config('rubyhdrdir')
		cpppath += ruby_hdrdir
		if version >= (2, 0, 0):
			cpppath += read_config('rubyarchhdrdir')
		cpppath += [os.path.join(ruby_hdrdir[0], read_config('arch')[0])]

	self.check(header_name='ruby.h', includes=cpppath, errmsg='could not find ruby header file', link_header_test=False)

	self.env.LIBPATH_RUBYEXT = read_config('libdir')
	self.env.LIBPATH_RUBYEXT += archdir
	self.env.INCLUDES_RUBYEXT = cpppath
	self.env.CFLAGS_RUBYEXT = read_config('CCDLFLAGS')
	self.env.rubyext_PATTERN = '%s.' + read_config('DLEXT')[0]

	# ok this is really stupid, but the command and flags are combined.
	# so we try to find the first argument...
	flags = read_config('LDSHARED')
	while flags and flags[0][0] != '-':
		flags = flags[1:]

	# we also want to strip out the deprecated ppc flags
	if len(flags) > 1 and flags[1] == "ppc":
		flags = flags[2:]

	self.env.LINKFLAGS_RUBYEXT = flags
	self.env.LINKFLAGS_RUBYEXT += read_config('LIBS')
	self.env.LINKFLAGS_RUBYEXT += read_config('LIBRUBYARG_SHARED')

	if Options.options.rubyarchdir:
		self.env.ARCHDIR_RUBY = Options.options.rubyarchdir
	else:
		self.env.ARCHDIR_RUBY = read_config('sitearchdir')[0]

	if Options.options.rubylibdir:
		self.env.LIBDIR_RUBY = Options.options.rubylibdir
	else:
		self.env.LIBDIR_RUBY = read_config('sitelibdir')[0]

@conf
def check_ruby_module(self, module_name):
	"""
	Check if the selected ruby interpreter can require the given ruby module::

		def configure(conf):
			conf.check_ruby_module('libxml')

	:param module_name: module
	:type  module_name: string
	"""
	self.start_msg('Ruby module %s' % module_name)
	try:
		self.cmd_and_log(self.env.RUBY + ['-e', 'require \'%s\';puts 1' % module_name])
	except Errors.WafError:
		self.end_msg(False)
		self.fatal('Could not find the ruby module %r' % module_name)
	self.end_msg(True)

@extension('.rb')
def process(self, node):
	return self.create_task('run_ruby', node)

class run_ruby(Task.Task):
	"""
	Task to run ruby files detected by file extension .rb::

		def options(opt):
			opt.load('ruby')

		def configure(ctx):
			ctx.check_ruby_version()

		def build(bld):
			bld.env.RBFLAGS = '-e puts "hello world"'
			bld(source='a_ruby_file.rb')
	"""
	run_str = '${RUBY} ${RBFLAGS} -I ${SRC[0].parent.abspath()} ${SRC}'

def options(opt):
	"""
	Add the ``--with-ruby-archdir``, ``--with-ruby-libdir`` and ``--with-ruby-binary`` options
	"""
	opt.add_option('--with-ruby-archdir', type='string', dest='rubyarchdir', help='Specify directory where to install arch specific files')
	opt.add_option('--with-ruby-libdir', type='string', dest='rubylibdir', help='Specify alternate ruby library path')
	opt.add_option('--with-ruby-binary', type='string', dest='rubybinary', help='Specify alternate ruby binary')