### # wxRuby3 buildtools configuration # Copyright (c) M.J.N. Corino, The Netherlands ### require 'rbconfig' require 'fileutils' require 'json' module FileUtils # add convenience methods def rmdir_if(list, **kwargs) list = fu_list(list).select { |path| File.exist?(path) } rmdir(list, **kwargs) unless list.empty? end def rm_if(list, **kwargs) list = fu_list(list).select { |path| File.exist?(path) } rm_f(list, **kwargs) unless list.empty? end end module WXRuby3 ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) if defined? ::RbConfig RB_CONFIG = ::RbConfig::CONFIG else RB_CONFIG = ::Config::CONFIG end unless defined? RB_CONFIG CFG_KEYS = %w[prefix bindir libdir datadir mandir sysconfdir localstatedir libruby librubyver librubyverarch siteruby siterubyver siterubyverarch rbdir sodir] RB_DEFAULTS = %w[bindir libdir datadir mandir sysconfdir localstatedir] CONFIG = { 'libruby' => File.join(RB_CONFIG['libdir'], 'ruby'), 'librubyver' => RB_CONFIG['rubylibdir'], 'librubyverarch' => RB_CONFIG['archdir'], 'siteruby' => RB_CONFIG['sitedir'], 'siterubyver' => RB_CONFIG['sitelibdir'], 'siterubyverarch' => RB_CONFIG['sitearchdir'], 'rbdir' => '$siterubyver', 'sodir' => '$siterubyverarch', } CFG_KEYS.concat(%w{wxwin wxxml wxwininstdir with-wxwin with-debug swig doxygen}) CONFIG.merge!({ 'wxwin' => ENV['WXWIN'] || '', 'wxxml' => ENV['WXXML'] || '', 'wxwininstdir' => '', 'with-wxwin' => !!ENV['WITH_WXWIN'], 'with-debug' => ((ENV['WXRUBY_DEBUG'] || '') == '1'), 'swig' => ENV['WXRUBY_SWIG'] || 'swig', 'doxygen' => ENV['WXRUBY_DOXYGEN'] || 'doxygen' }) BUILD_CFG = '.wxconfig' # Ruby 2.5 is the minimum version for wxRuby3 __rb_ver = RUBY_VERSION.split('.').collect {|v| v.to_i} if (__rb_major = __rb_ver.shift) < 2 || (__rb_major == 2 && __rb_ver.shift < 5) STDERR.puts 'ERROR: wxRuby3 requires Ruby >= 2.5.0!' exit(1) end # Pure-ruby lib files ALL_RUBY_LIB_FILES = FileList[ 'lib/**/*.rb' ] # The version file VERSION_FILE = File.join(ROOT,'lib', 'wx', 'version.rb') # Setting the version via an environment variable if ENV['WXRUBY_VERSION'] WXRUBY_VERSION = ENV['WXRUBY_VERSION'] File.open(VERSION_FILE, 'w') do | version_file | version_file.puts "module Wx" version_file.puts " WXRUBY_VERSION = '#{WXRUBY_VERSION}#{ENV['WXRUBY_RELEASE_TYPE'] || ''}'" version_file.puts "end" end # Try loading the existing version file elsif File.exist?(VERSION_FILE) require VERSION_FILE WXRUBY_VERSION = Wx::WXRUBY_VERSION # Leave version undefined else WXRUBY_VERSION = '' end WXWIN_MINIMUM = '3.2.0' module Config def do_run(*cmd, capture: nil) output = nil if capture env_bup = exec_env.keys.inject({}) do |h, ev| h[ev] = ENV[ev] ? ENV[ev].dup : nil h end case capture when :out # default when :no_err # redirect stderr to null sink cmd << '2> ' << (windows? ? 'NULL' : '/dev/null') when :err, :all cmd << '2>&1' end begin # setup ENV for child execution ENV.merge!(Config.instance.exec_env) output = `#{cmd.join(' ')}` ensure # restore ENV env_bup.each_pair do |k,v| if v ENV[k] = v else ENV.delete(k) end end end else Rake.sh(exec_env, *cmd, verbose: verbose?) end output end private :do_run def make_ruby_cmd(*cmd) [ FileUtils::RUBY, '-I', rb_lib_path, (verbose? ? '-v' : nil), *cmd.flatten ].compact end private :make_ruby_cmd def execute(*cmd) do_run(*cmd.flatten) end def run(*cmd, capture: nil) do_run(*make_ruby_cmd(cmd), capture: capture) end def debug_command(*args) raise "Do not know how to debug for platform #{platform}" end def debug(*args, **options) args.unshift("-I#{File.join(Config.wxruby_root, 'lib')}") Rake.sh(exec_env, debug_command(*args), **options) end def respawn_rake(argv = ARGV) Kernel.exec($0, *argv) end def expand(cmd) `#{cmd}` end private :expand def sh(*cmd, **kwargs) Rake.sh(*cmd, **kwargs) { |ok,_| !!ok } end private :sh alias :bash :sh private :bash def test(*tests, **options) tests = Dir.glob(File.join(Config.instance.test_dir, '*.rb')) if tests.empty? tests.each do |test| unless File.exist?(test) test = File.join(Config.instance.test_dir, test) test = Dir.glob(test+'.rb').shift || test unless File.exist?(test) end Rake.sh(Config.instance.exec_env, *make_ruby_cmd(test)) end end def irb(**options) irb_cmd = File.join(File.dirname(FileUtils::RUBY), 'irb') Rake.sh(Config.instance.exec_env, *make_ruby_cmd('-x', irb_cmd), **options) end def check_git if expand("which git 2>/dev/null").chomp.empty? STDERR.puts 'ERROR: Need GIT installed to run wxRuby3 bootstrap!' exit(1) end end def check_doxygen if expand("which #{get_config('doxygen')} 2>/dev/null").chomp.empty? STDERR.puts "ERROR: Cannot find #{get_config('doxygen')}. Need Doxygen installed to run wxRuby3 bootstrap!" exit(1) end end def check_wx_config false end def wx_config(_option) nil end def get_config(key) Config.get_config(key) end def get_cfg_string(key) Config.get_cfg_string(key) end def set_config(key, val) Config.set_config(key, val) end def dll_mask "#{dll_ext}*" end def do_link(_pkg) end def check_rpath_patch true end def patch_rpath(_shlib, _rpath) true end class AnyOf def initialize(*features) @features = features end attr_reader :features def hash @features.hash end def eql?(other) self.class === other && @features.eql?(other.features) end end class << self def rb_version @rb_version ||= RUBY_VERSION.split('.').collect {|n| n.to_i} end def rb_ver_major rb_version[0] end def rb_ver_minor rb_version[1] end def rb_ver_release rb_version[2] end def build_cfg File.join(WXRuby3::ROOT, WXRuby3::BUILD_CFG) end def save File.open(build_cfg, 'w') do |f| f << JSON.pretty_generate(WXRuby3::CONFIG) end end def load if File.file?(build_cfg) File.open(build_cfg, 'r') do |f| WXRuby3::CONFIG.merge!(JSON.load(f.read)) end end end def wxruby_root WXRuby3::ROOT end def platform case RUBY_PLATFORM when /mingw/ :mingw when /cygwin/ :cygwin when /netbsd/ :netbsd when /darwin/ :macosx when /linux/ :linux else :unknown end end def create load # load the build config (if any) klass = Class.new do include Config include FileUtils def initialize @ruby_exe = RB_CONFIG["ruby_install_name"] @extmk = /extmk\.rb/ =~ $0 @platform = Config.platform require File.join(File.dirname(__FILE__), 'config', @platform.to_s) self.class.include(WXRuby3::Config::Platform) init # initialize settings end attr_reader :ruby_exe, :extmk, :platform, :helper_modules, :helper_inits, :include_modules, :verbosity attr_reader :release_build, :debug_build, :verbose_debug, :no_deprecate attr_reader :ruby_cppflags, :ruby_ldflags, :ruby_libs, :extra_cflags, :extra_cppflags, :extra_ldflags, :extra_libs, :cpp_out_flag, :link_output_flag, :obj_ext, :dll_ext, :cxxflags, :libs, :cpp, :ld, :verbose_flag attr_reader :wx_port, :wx_path, :wx_cppflags, :wx_libs, :wx_setup_h, :wx_xml_path attr_reader :swig_major, :swig_dir, :swig_path, :src_dir, :src_path, :src_gen_dir, :src_gen_path, :obj_dir, :obj_path, :rake_deps_dir, :rake_deps_path, :dest_dir, :test_dir, :classes_dir, :classes_path, :common_dir, :common_path, :interface_dir, :interface_path, :ext_dir, :ext_path, :wxruby_dir, :wxruby_path, :exec_env attr_reader :rb_lib_dir, :rb_lib_path, :rb_events_dir, :rb_events_path, :rb_doc_dir, :rb_doc_path, :rb_docgen_dir, :rb_docgen_path # (re-)initialize settings def init # STANDARD DIRECTORIES @ext_dir = 'ext' @ext_path = File.join(Config.wxruby_root, @ext_dir) @wxruby_dir = File.join(@ext_dir, 'wxruby3') @wxruby_path = File.join(@ext_path, 'wxruby3') @swig_dir = File.join(@wxruby_dir,'swig') @swig_path = File.join(Config.wxruby_root, @swig_dir) @rake_deps_dir = File.join('rakelib', 'deps') @rake_deps_path = File.join(Config.wxruby_root, @rake_deps_dir) @src_dir = File.join(@wxruby_dir,'src') @src_path = File.join(Config.wxruby_root, @src_dir) @src_gen_dir = File.join(@src_dir, '.generate') @src_gen_path = File.join(Config.wxruby_root, @src_gen_dir) @obj_dir = File.join(@wxruby_dir,'obj') @obj_path = File.join(Config.wxruby_root, @obj_dir) @dest_dir = File.join(Config.wxruby_root, 'lib') @test_dir = File.join(Config.wxruby_root, 'tests') @classes_dir = File.join(@swig_dir, 'classes') @classes_path = File.join(Config.wxruby_root, @classes_dir) @common_dir = File.join(@classes_dir, 'common') @common_path = File.join(Config.wxruby_root, @common_dir) @interface_dir = File.join(@classes_dir, 'include') @interface_path = File.join(Config.wxruby_root, @interface_dir) @rb_lib_dir = 'lib' @rb_lib_path = File.join(Config.wxruby_root, @rb_lib_dir) @rb_doc_dir = File.join(@rb_lib_dir, 'wx', 'doc') @rb_doc_path = File.join(Config.wxruby_root, @rb_doc_dir) @rb_docgen_dir = File.join(@rb_doc_dir, 'gen') @rb_docgen_path = File.join(Config.wxruby_root, @rb_docgen_dir) # Extra swig helper files to be built @helper_modules = if macosx? %w|RubyStockObjects Mac| else %w|RubyStockObjects| end # helper to initialize on startup (stock objects can only be initialized after App creation) @helper_inits = @helper_modules - %w|RubyStockObjects| # included swig specfiles not needing standalone processing @include_modules = %w|common.i typedefs.i typemap.i mark_free_impl.i memory_management.i shared/*.i|.collect do |glob| Dir.glob(File.join(@swig_dir, glob)) end.flatten @debug_build = WXRuby3::CONFIG['with-debug'] @release_build = !@debug_build @verbosity = ENV['WXRUBY_VERBOSE'] ? (ENV['WXRUBY_VERBOSE'] || '1').to_i : 0 @dynamic_build = !!ENV['WXRUBY_DYNAMIC'] @static_build = !!ENV['WXRUBY_STATIC'] @no_deprecate = !(!!ENV['WX_KEEP_DEPRECATE']) @ruby_includes = [ RB_CONFIG["rubyhdrdir"], RB_CONFIG["sitehdrdir"], RB_CONFIG["vendorhdrdir"], File.join(RB_CONFIG["rubyhdrdir"], RB_CONFIG['arch']) ].compact @ruby_includes << File.join(@wxruby_path, 'include') @ruby_cppflags = [RB_CONFIG["CFLAGS"]].compact @ruby_ldflags = [RB_CONFIG['LDFLAGS'], RB_CONFIG['DLDFLAGS'], RB_CONFIG['ARCHFLAG']].compact @ruby_libs = [] @extra_cppflags = ['-DSWIG_TYPE_TABLE=wxruby3'] @extra_cflags = [] @extra_ldflags = [] @extra_libs = [] @cpp_out_flag = '-o ' @link_output_flag = '-o ' @obj_ext = RB_CONFIG["OBJEXT"] @dll_ext = RB_CONFIG['DLEXT'] # Exclude certian classes from being built, even if they are present # in the configuration of wxWidgets. if ENV['WXRUBY_EXCLUDED'] ENV['WXRUBY_EXCLUDED'].split(",").each { |classname| exclude_module(classname) } end @exec_env = {} # platform specific initialization init_platform if @wx_xml_path.empty? @wx_xml_path = File.join(@ext_path, 'wxWidgets', 'docs', 'doxygen', 'out', 'xml') end @verbose_flag = '' if @debug_build @verbose_flag << '-D__WXRB_DEBUG__=1' end # SIXTH: Putting it all together # Flags to be passed to the C++ compiler @cxxflags = [@wx_cppflags, @ruby_cppflags, @extra_cflags, @extra_cppflags ].flatten.join(' ') # Flags to be passed to the linker @ldflags = [ @ruby_ldflags, @extra_ldflags ].flatten.join(' ') # Libraries that the linker should build @libs = [ @wx_libs, @ruby_libs, @extra_libs ].flatten.join(' ') end def report if @debug_build puts "Enabled DEBUG build" puts "Enabled debugging output" else puts "Enabled RELEASE build" end end def verbose? @verbosity>0 end def is_configured? File.file?(File.join(WXRuby3::ROOT, WXRuby3::BUILD_CFG)) end def is_bootstrapped? is_configured? && File.directory?(wx_xml_path) end def with_wxwin? get_config('with-wxwin') end def wx_version @wx_version || '' end def wx_abi_version @wx_abi_version || '' end def cygwin? @platform == :cygwin end def mingw? @platform == :mingw end def netbsd? @platform == :netbsd end def macosx? @platform == :macosx end def linux? @platform == :linux end def windows? mingw? || cygwin? end def ldflags(_target) @ldflags end def has_wxwidgets_xml? File.directory?(@wx_xml_path) end def build_paths [ rake_deps_path, src_path, src_gen_path, obj_path, classes_path, common_path, interface_path ] end def do_bootstrap check_doxygen # do we have a local wxWidgets tree already? unless File.directory?(File.join(ext_path, 'wxWidgets', 'docs', 'doxygen')) wx_checkout end # do we need to build wxWidgets? if get_config('with-wxwin') && get_cfg_string('wxwin').empty? chdir(File.join(ext_path, 'wxWidgets')) do wx_build end end # generate the doxygen XML output wx_generate_xml # now we need to respawn the rake command in place of this process respawn_rake end # Testing the relevant wxWidgets setup.h file to see what # features are supported. # The wxWidgets setup.h file contains a series of definitions like # #define wxUSE_FOO 1. The location of the file should be set # by the platform-specific rakefile. Parse it into a ruby hash: def features @features ||= _retrieve_features(wx_setup_h) end def any_feature_set?(*featureset) featureset.any? do |feature| if ::Array === feature features_set?(*feature) else !!features[feature.to_s] end end end private :any_feature_set? def features_set?(*featureset) featureset.all? do |feature| if AnyOf === feature any_feature_set?(*feature.features) else !!features[feature.to_s] end end end def excluded_module?(module_spec) explicit_excluded_modules.include?(module_spec.module_name) || !features_set?(*module_spec.requirements) end def exclude_module(module_name) explicit_excluded_modules << module_name end private def explicit_excluded_modules @explicit_excluded_modules ||= [] end def _retrieve_features(wxwidgets_setup_h) features = {} File.read(wxwidgets_setup_h).scan(/^\s*#define\s+(wx\w+|__\w+__)\s+([01])/) do | define | features[$1] = $2.to_i.zero? ? false : true end if is_configured? && wxwidgets_setup_h features end end klass.new end private :create def instance unless @instance @instance = create end @instance end def get_config(key) v = if WXRuby3::CONFIG.has_key?(key.to_s) WXRuby3::CONFIG[key.to_s] else RB_DEFAULTS.include?(key.to_s) ? RB_CONFIG[key.to_s] : nil end v = WXRuby3::CONFIG[v[1,v.size]] while String === v && v.start_with?('$') && WXRuby3::CONFIG.has_key?(v[1,v.size]) v end def get_cfg_string(key) get_config(key) || '' end def set_config(key, val) WXRuby3::CONFIG[key.to_s] = val end def is_configured? instance.is_configured? end def is_bootstrapped? instance.is_bootstrapped? end end # class << self end # module Config def self.build_cfg Config.build_cfg end def self.config Config.instance end def self.is_configured? Config.is_configured? end def self.is_bootstrapped? Config.is_bootstrapped? end end # module WXRuby3 Dir.glob(File.join(File.dirname(__FILE__), 'ext', '*.rb')).each do |fn| require fn end