The Availability class is concerned with libraries, headers, and functions. It can be easily wrapped (see Mkrf::Generator for an example) and should be able to be used as a basis for a variety of programs which need to determine functionality based on what libraries are available on the current system.
These really shouldn’t be static like this..
Create a new Availability instance.
Valid keys for the options hash include:
:loaded_libs — libraries to load by default
:library_paths — libraries paths to include by default
:headers — headers to load by default
:compiler — which compiler to use when determining availability
:includes — directories that should be searched for include files
# File lib/mkrf/availability.rb, line 37 37: def initialize(options = {}) 38: @loaded_libs = [(options[:loaded_libs] || Config::CONFIG["LIBS"].gsub('-l', '').split)].flatten 39: @library_paths = [(options[:library_paths] || [])].flatten 40: # Not sure what COMMON_HEADERS looks like when populated 41: @headers = options[:headers] || [] # Config::CONFIG["COMMON_HEADERS"] 42: @compiler = options[:compiler] || Config::CONFIG["CC"] 43: @includes = [(options[:includes] || DEFAULT_INCLUDES)].flatten 44: @logger = Logger.new('mkrf.log') 45: @defines = [] 46: end
Returns the result of an attempt to compile and link the function body passed in
# File lib/mkrf/availability.rb, line 126 126: def can_link?(function_body) 127: silence_command_line do 128: create_source(function_body) 129: system(link_command) 130: end 131: ensure 132: FileUtils.rm_f TEMP_SOURCE_FILE 133: FileUtils.rm_f TEMP_EXECUTABLE 134: end
Takes the name of an executable and an optional set of paths to search. If no paths are given, the environmental path is used by default. Returns the absolute path to an executable, or nil if not found.
# File lib/mkrf/availability.rb, line 190 190: def find_executable(bin, *paths) 191: paths = ENV['PATH'].split(File::PATH_SEPARATOR) if paths.empty? 192: paths.each do |path| 193: file = File.join(path, bin) 194: return file if File.executable?(file) 195: end 196: return nil 197: end
Returns true if the function is able to be called based on libraries and headers currently loaded. Returns false otherwise.
Params:
function — the function to check for
# File lib/mkrf/availability.rb, line 114 114: def has_function?(function) 115: if can_link?(simple_call(function)) or can_link?(simple_reference(function)) 116: logger.info "Function found: #{function}()" 117: return true 118: else 119: logger.warn "Function not found: #{function}()" 120: return false 121: end 122: end
Returns true if the header is found in the default search path or in optional paths passed as an argument, false otherwise. If the header is found, the preprocessor constant HAVE_BLAH is defined where BLAH is the name of the header in uppercase without the file extension.
Params:
header — the header to be searched for
paths — an optional list of search paths if the header is not found in the default paths
# File lib/mkrf/availability.rb, line 98 98: def has_header?(header, *paths) 99: if header_already_loaded?(header) || header_can_link?(header) || 100: header_found_in_paths?(header, paths) 101: defines << format("HAVE_%s", header.tr("a-z./\0055", "A-Z___")) 102: return true 103: end 104: 105: logger.warn "Header not found: #{header}" 106: return false 107: end
Returns a boolean whether indicating whether the library can be found by attempting to reference the function passed (main by default).
Params:
library — the library to be included as a string
function — a method to base the inclusion of the library on. main by default.
paths — an optional list of search paths if the library is not found in the default paths
# File lib/mkrf/availability.rb, line 81 81: def has_library?(library, function = "main", *paths) 82: logger.info "Checking for library: #{library}" 83: return true if library_already_loaded?(library) 84: return true if RUBY_PLATFORM =~ /mswin/ # TODO: find a way on windows 85: # Should this be only found_library? or a specialized version with 86: # path searching? 87: found_library?(library, function) 88: end
Include a header in the list of availiable headers. Returns false if the header is not available. Returns non-false otherwise. If the header is found, the preprocessor constant HAVE_BLAH is defined where BLAH is the name of the header in uppercase without the file extension.
Params:
header — the name of the header to be included as a string.
paths — an optional list of search paths if the header is not found in the default paths.
# File lib/mkrf/availability.rb, line 70 70: def include_header(header, *paths) 71: @headers << header if has_header?(header, *paths) 72: end
Include a library in the list of available libs. Returns false if the library is not available. Returns non-false otherwise.
Params:
library — the library to be included as a string.
function — a method to base the inclusion of the library on. main by default.
paths — an optional list of search paths if the library is not found in the default paths.
# File lib/mkrf/availability.rb, line 55 55: def include_library(library, function = "main", *paths) 56: paths.each do |library_dir| 57: @library_paths << library_dir 58: end 59: @loaded_libs << library if has_library?(library, function) 60: end
Returns a string of include directories formatted for compilation
# File lib/mkrf/availability.rb, line 183 183: def includes_compile_string 184: @includes.collect {|i| "-I#{i}"}.join(' ') 185: end
Returns a string of libraries formatted for compilation
# File lib/mkrf/availability.rb, line 149 149: def library_compile_string 150: if RUBY_PLATFORM =~ /mswin/ 151: @loaded_libs.join(' ') 152: else 153: @loaded_libs.collect {|l| "-l#{l}"}.join(' ') 154: end 155: end
Returns a string of libraries directories formatted for compilation
# File lib/mkrf/availability.rb, line 158 158: def library_paths_compile_string 159: if RUBY_PLATFORM =~ /mswin/ 160: @library_paths.collect {|l| "/libpath:#{l}"}.join(' ') 161: else 162: @library_paths.collect {|l| "-L#{l}"}.join(' ') 163: end 164: end
# File lib/mkrf/availability.rb, line 136 136: def with_headers(*args, &b) 137: with_stackable_attribute('headers', *args, &b) 138: end
Creates a temporary source file with the string passed
# File lib/mkrf/availability.rb, line 288 288: def create_source(src) 289: File.open(TEMP_SOURCE_FILE, "w+") do |f| 290: f.write(src) 291: end 292: end
# File lib/mkrf/availability.rb, line 201 201: def found_library?(library, function) 202: library_found = with_loaded_libs(library) { 203: has_function? function 204: } 205: 206: library_found ? logger.info("Library found: #{library}") : 207: logger.warn("Library not found: #{library}") 208: 209: library_found 210: end
# File lib/mkrf/availability.rb, line 232 232: def header_already_loaded?(header) 233: if @headers.include? header 234: logger.info("Header already loaded: #{header}") 235: return true 236: end 237: 238: return false 239: end
# File lib/mkrf/availability.rb, line 212 212: def header_can_link?(header) 213: has_header = with_headers(header) { 214: can_link?(simple_include(header)) 215: } 216: 217: if has_header 218: logger.info("Header found: #{header}") 219: return true 220: end 221: end
def library_found_in_paths?(library, paths)
paths.each do |include_path| if with_libs(include_path) { library_can_link?(header) } @libspath << include_path return true end end return false
end
# File lib/mkrf/availability.rb, line 254 254: def header_found_in_paths?(header, paths) 255: paths.each do |include_path| 256: if with_includes(include_path) { header_can_link?(header) } 257: @includes << include_path 258: return true 259: end 260: end 261: 262: return false 263: end
# File lib/mkrf/availability.rb, line 275 275: def header_include_string 276: @headers.collect {|header| "#include <#{header}>"}.join('\n') 277: end
# File lib/mkrf/availability.rb, line 223 223: def library_already_loaded?(library) 224: if @loaded_libs.include? library 225: logger.info "Library already loaded: #{library}" 226: return true 227: end 228: 229: return false 230: end
# File lib/mkrf/availability.rb, line 279 279: def link_command 280: # This current implementation just splats the library_paths in 281: # unconditionally. Is this problematic? 282: "#{@compiler} -o #{TEMP_EXECUTABLE} #{library_paths_compile_string}" + 283: " #{library_compile_string} #{includes_compile_string}" + 284: " #{TEMP_SOURCE_FILE}" 285: end
# File lib/mkrf/availability.rb, line 321 321: def silence_command_line 322: yield and return if $debug 323: silence_stream(STDERR) do 324: silence_stream(STDOUT) do 325: yield 326: end 327: end 328: end
Silences any stream for the duration of the block.
silence_stream(STDOUT) do puts 'This will never be seen' end puts 'But this will'
# File lib/mkrf/availability.rb, line 339 339: def silence_stream(stream) 340: old_stream = stream.dup 341: stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null') 342: stream.sync = true 343: yield 344: ensure 345: stream.reopen(old_stream) 346: end
Basic skeleton for calling a function
# File lib/mkrf/availability.rb, line 295 295: def simple_call(func) 296: src = #{header_include_string} int main() { return 0; } int t() { #{func}(); return 0; } 297: end
skeleton for testing includes
# File lib/mkrf/availability.rb, line 313 313: def simple_include(header) 314: src = #{header_include_string} #include <#{header}> int main() { return 0; } 315: end
Basic skeleton for referencing a function
# File lib/mkrf/availability.rb, line 304 304: def simple_reference(func) 305: src = #{header_include_string} int main() { return 0; } int t() { void ((*volatile p)()); p = (void ((*)()))#{func}; return 0; } 306: end
# File lib/mkrf/availability.rb, line 265 265: def with_stackable_attribute(attribute, *args) 266: args = args.to_a 267: instance_variable_set("@#{attribute}", 268: instance_variable_get("@#{attribute}") + args) 269: value = yield 270: instance_variable_set("@#{attribute}", 271: instance_variable_get("@#{attribute}") - args) 272: return value 273: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.