1#!/usr/bin/env python 2# encoding: utf-8 3# daniel.svensson at purplescout.se 2008 4# Thomas Nagy 2016-2018 (ita) 5 6""" 7Support for Ruby extensions. A C/C++ compiler is required:: 8 9 def options(opt): 10 opt.load('compiler_c ruby') 11 def configure(conf): 12 conf.load('compiler_c ruby') 13 conf.check_ruby_version((1,8,0)) 14 conf.check_ruby_ext_devel() 15 conf.check_ruby_module('libxml') 16 def build(bld): 17 bld( 18 features = 'c cshlib rubyext', 19 source = 'rb_mytest.c', 20 target = 'mytest_ext', 21 install_path = '${ARCHDIR_RUBY}') 22 bld.install_files('${LIBDIR_RUBY}', 'Mytest.rb') 23""" 24 25import os 26from waflib import Errors, Options, Task, Utils 27from waflib.TaskGen import before_method, feature, extension 28from waflib.Configure import conf 29 30@feature('rubyext') 31@before_method('apply_incpaths', 'process_source', 'apply_bundle', 'apply_link') 32def init_rubyext(self): 33 """ 34 Add required variables for ruby extensions 35 """ 36 self.install_path = '${ARCHDIR_RUBY}' 37 self.uselib = self.to_list(getattr(self, 'uselib', '')) 38 if not 'RUBY' in self.uselib: 39 self.uselib.append('RUBY') 40 if not 'RUBYEXT' in self.uselib: 41 self.uselib.append('RUBYEXT') 42 43@feature('rubyext') 44@before_method('apply_link', 'propagate_uselib_vars') 45def apply_ruby_so_name(self): 46 """ 47 Strip the *lib* prefix from ruby extensions 48 """ 49 self.env.cshlib_PATTERN = self.env.cxxshlib_PATTERN = self.env.rubyext_PATTERN 50 51@conf 52def check_ruby_version(self, minver=()): 53 """ 54 Checks if ruby is installed. 55 If installed the variable RUBY will be set in environment. 56 The ruby binary can be overridden by ``--with-ruby-binary`` command-line option. 57 """ 58 59 ruby = self.find_program('ruby', var='RUBY', value=Options.options.rubybinary) 60 61 try: 62 version = self.cmd_and_log(ruby + ['-e', 'puts defined?(VERSION) ? VERSION : RUBY_VERSION']).strip() 63 except Errors.WafError: 64 self.fatal('could not determine ruby version') 65 self.env.RUBY_VERSION = version 66 67 try: 68 ver = tuple(map(int, version.split('.'))) 69 except Errors.WafError: 70 self.fatal('unsupported ruby version %r' % version) 71 72 cver = '' 73 if minver: 74 cver = '> ' + '.'.join(str(x) for x in minver) 75 if ver < minver: 76 self.fatal('ruby is too old %r' % ver) 77 78 self.msg('Checking for ruby version %s' % cver, version) 79 80@conf 81def check_ruby_ext_devel(self): 82 """ 83 Check if a ruby extension can be created 84 """ 85 if not self.env.RUBY: 86 self.fatal('ruby detection is required first') 87 88 if not self.env.CC_NAME and not self.env.CXX_NAME: 89 self.fatal('load a c/c++ compiler first') 90 91 version = tuple(map(int, self.env.RUBY_VERSION.split("."))) 92 93 def read_out(cmd): 94 return Utils.to_list(self.cmd_and_log(self.env.RUBY + ['-rrbconfig', '-e', cmd])) 95 96 def read_config(key): 97 return read_out('puts RbConfig::CONFIG[%r]' % key) 98 99 cpppath = archdir = read_config('archdir') 100 101 if version >= (1, 9, 0): 102 ruby_hdrdir = read_config('rubyhdrdir') 103 cpppath += ruby_hdrdir 104 if version >= (2, 0, 0): 105 cpppath += read_config('rubyarchhdrdir') 106 cpppath += [os.path.join(ruby_hdrdir[0], read_config('arch')[0])] 107 108 self.check(header_name='ruby.h', includes=cpppath, errmsg='could not find ruby header file', link_header_test=False) 109 110 self.env.LIBPATH_RUBYEXT = read_config('libdir') 111 self.env.LIBPATH_RUBYEXT += archdir 112 self.env.INCLUDES_RUBYEXT = cpppath 113 self.env.CFLAGS_RUBYEXT = read_config('CCDLFLAGS') 114 self.env.rubyext_PATTERN = '%s.' + read_config('DLEXT')[0] 115 116 # ok this is really stupid, but the command and flags are combined. 117 # so we try to find the first argument... 118 flags = read_config('LDSHARED') 119 while flags and flags[0][0] != '-': 120 flags = flags[1:] 121 122 # we also want to strip out the deprecated ppc flags 123 if len(flags) > 1 and flags[1] == "ppc": 124 flags = flags[2:] 125 126 self.env.LINKFLAGS_RUBYEXT = flags 127 self.env.LINKFLAGS_RUBYEXT += read_config('LIBS') 128 self.env.LINKFLAGS_RUBYEXT += read_config('LIBRUBYARG_SHARED') 129 130 if Options.options.rubyarchdir: 131 self.env.ARCHDIR_RUBY = Options.options.rubyarchdir 132 else: 133 self.env.ARCHDIR_RUBY = read_config('sitearchdir')[0] 134 135 if Options.options.rubylibdir: 136 self.env.LIBDIR_RUBY = Options.options.rubylibdir 137 else: 138 self.env.LIBDIR_RUBY = read_config('sitelibdir')[0] 139 140@conf 141def check_ruby_module(self, module_name): 142 """ 143 Check if the selected ruby interpreter can require the given ruby module:: 144 145 def configure(conf): 146 conf.check_ruby_module('libxml') 147 148 :param module_name: module 149 :type module_name: string 150 """ 151 self.start_msg('Ruby module %s' % module_name) 152 try: 153 self.cmd_and_log(self.env.RUBY + ['-e', 'require \'%s\';puts 1' % module_name]) 154 except Errors.WafError: 155 self.end_msg(False) 156 self.fatal('Could not find the ruby module %r' % module_name) 157 self.end_msg(True) 158 159@extension('.rb') 160def process(self, node): 161 return self.create_task('run_ruby', node) 162 163class run_ruby(Task.Task): 164 """ 165 Task to run ruby files detected by file extension .rb:: 166 167 def options(opt): 168 opt.load('ruby') 169 170 def configure(ctx): 171 ctx.check_ruby_version() 172 173 def build(bld): 174 bld.env.RBFLAGS = '-e puts "hello world"' 175 bld(source='a_ruby_file.rb') 176 """ 177 run_str = '${RUBY} ${RBFLAGS} -I ${SRC[0].parent.abspath()} ${SRC}' 178 179def options(opt): 180 """ 181 Add the ``--with-ruby-archdir``, ``--with-ruby-libdir`` and ``--with-ruby-binary`` options 182 """ 183 opt.add_option('--with-ruby-archdir', type='string', dest='rubyarchdir', help='Specify directory where to install arch specific files') 184 opt.add_option('--with-ruby-libdir', type='string', dest='rubylibdir', help='Specify alternate ruby library path') 185 opt.add_option('--with-ruby-binary', type='string', dest='rubybinary', help='Specify alternate ruby binary') 186 187