# encoding: utf-8require'phusion_passenger/platform_info/ruby'require'phusion_passenger/platform_info/linux'require'phusion_passenger/platform_info/compiler'require'phusion_passenger/platform_info/operating_system'require'phusion_passenger/utils/ansi_colors'modulePhusionPassengermodulePlatformInfo# Almost all software require other software in order to run. We call those# other software 'dependencies'. Reliably checking for dependencies can be# difficult. Helping the user in case a dependency is not installed (or# doesn't seem to be installed) is more difficult still.# # The Depcheck framework seeks to make all this easier. It allows the programmer# to write "specs" which contain dependency checking code in a structured way.# The programmer defines a dependency's basic information (name, website, etc),# defines installation instructions (which may be customized per platform) and# defines code for checking whether the dependency actually exists. The Depcheck# framework:# # * Provides helpers for checking for the existance of commands, libraries,# headers, etc.# * Registers all dependency specs in a way that can be easily accessed# structurally.# * Allows user-friendly display of dependency checking progress and user help# instructions.# # Most dependency checking code (e.g. autoconf) is very straightforward: they# just check for the existance of a command, library, header, etc and either# report "found" or "not found". In our experience the world is unfortunately# not that simple. Users can have multiple versions of a dependency installed,# where some dependencies are suitable while others are not. Therefore specs# should print as many details about the dependency as possible (location, version,# etc) so that the user can override any decisions if necessary.moduleDepcheckTHIS_DIR=File.expand_path(File.dirname(__FILE__))@@loaded={}@@database={}defself.load(partial_filename)if!@@loaded[partial_filename]filename="#{THIS_DIR}/#{partial_filename}.rb"content=File.read(filename)instance_eval(content,filename)@@loaded[partial_filename]=trueendenddefself.define(identifier,&block)@@database[identifier.to_s]=blockenddefself.find(identifier)# We lazy-initialize everything in order to save resources. This also# allows blocks to perform relatively expensive checks without hindering# startup time.identifier=identifier.to_sresult=@@database[identifier]ifresult.is_a?(Proc)result=Dependency.new(&result)@@database[identifier]=resultendresultendclassDependencydefinitialize(&block)instance_eval(&block)check_syntax_aspect("Name must be given"){!!@name}check_syntax_aspect("A checker must be given"){!!@checker}enddefcheck@install_comments=nil@check_result||=@checker.callend### DSL for specs ###defname(value=nil)value?@name=value:@nameenddefwebsite(value=nil)value?@website=value:@websiteenddefwebsite_comments(value=nil)value?@website_comments=value:@website_commentsenddefinstall_instructions(value=nil)ifvalue@install_instructions=valueelseif@install_instructions@install_instructionselsif@websiteresult="Please download it from <b>#{@website}</b>"result<<"\n(#{@website_comments})"if@website_commentsresultelse"Search Google for '#{@name}'."endendenddefinstall_comments(value=nil)value?@install_comments=value:@install_commentsendprivatedefcheck_syntax_aspect(description)if!yieldraisedescriptionendend### DSL for specs ###defdefine_checker(&block)@checker=blockenddefcheck_for_command(name,*args)result=find_command(name,*args)ifresult{:found=>true,"Location"=>result}elsefalseendenddefcheck_for_ruby_tool(name)result=locate_ruby_tool(name)ifresult{:found=>true,"Location"=>result}elsefalseendenddefcheck_for_header(header_name,language=:c,flags=nil)ifresult=PlatformInfo.find_header(header_name,language,flags){:found=>true,"Location"=>result}elsefalseendend# def check_for_library(name)# check_by_compiling("int main() { return 0; }", :cxx, nil, "-l#{name}")# end# def check_by_compiling(source, language = :c, cflags = nil, linkflags = nil)# case language# when :c# source_file = "#{PlatformInfo.tmpexedir}/depcheck-#{Process.pid}-#{Thread.current.object_id}.c"# compiler = "gcc"# compiler_flags = ENV['CFLAGS']# when :cxx# source_file = "#{PlatformInfo.tmpexedir}/depcheck-#{Process.pid}-#{Thread.current.object_id}.cpp"# compiler = "g++"# compiler_flags = "#{ENV['CFLAGS']} #{ENV['CXXFLAGS']}".strip# else# raise ArgumentError, "Unknown language '#{language}"# end# output_file = "#{PlatformInfo.tmpexedir}/depcheck-#{Process.pid}-#{Thread.current.object_id}"# begin# File.open(source_file, 'w') do |f|# f.puts(source)# end# if find_command(compiler)# command = "#{compiler} #{compiler_flags} #{cflags} " +# "#{source_file} -o #{output_file} #{linkflags}"# [!!system(command)]# else# [:unknown, "Cannot check: compiler '#{compiler}' not found."]# end# ensure# File.unlink(source_file) rescue nil# File.unlink(output_file) rescue nil# end# enddefcheck_for_ruby_library(name)beginrequire(name){:found=>true}rescueLoadErrorifdefined?(Gem)falseelsebeginrequire'rubygems'require(name){:found=>true}rescueLoadErrorfalseendendendenddefon(platform)returnif@on_invokedinvoke=falseif(linux_distro_tags||[]).include?(platform)invoke=trueelsecaseplatformwhen:linuxinvoke=trueifPlatformInfo.os_name=~/linux/when:freebsdinvoke=trueifPlatformInfo.os_name=~/freebsd/when:macosxinvoke=trueifPlatformInfo.os_name=="macosx"when:solarisinvoke=trueifPlatformInfo.os_name=~/solaris/when:other_platformsinvoke=trueendendifinvokeyield@on_invoked=trueendenddefapt_get_install(package_name)install_instructions("Please install it with <b>apt-get install #{package_name}</b>")enddefurpmi(package_name)install_instructions("Please install it with <b>urpmi #{package_name}</b>")enddefyum_install(package_name)install_instructions("Please install it with <b>yum install #{package_name}</b>")enddefemerge(package_name)install_instructions("Please install it with <b>emerge -av #{package_name}</b>")enddefgem_install(package_name)install_instructions("Please make sure RubyGems is installed, then run "+"<b>#{gem_command||'gem'} install #{package_name}</b>")enddefxcode_install(component)install_instructions("Please install <b>Xcode</b>, then in Xcode go to "+"<b>Preferences -> Downloads -> Components</b> and install <b>#{component}</b>")enddefruby_commandPlatformInfo.ruby_commandenddefgem_commandPlatformInfo.gem_commandenddeffind_command(command,*args)PlatformInfo.find_command(command,*args)enddeflinux_distro_tagsPlatformInfo.linux_distro_tagsenddeflocate_ruby_tool(name)PlatformInfo.locate_ruby_tool(name)endend# class DependencyclassConsoleRunnerattr_reader:missing_dependenciesdefinitialize@stdout=STDOUT@dep_identifiers=[]enddefadd(identifier)@dep_identifiers<<identifierenddefcheck_allold_log_impl=PlatformInfo.log_implementationbeginPlatformInfo.log_implementation=lambdado|message|message=PlatformInfo.send(:reindent,message,10)message.sub!(/^ /,'')STDOUT.puts" -> #{message}"end@missing_dependencies=[]@dep_identifiers.eachdo|identifier|dep=Depcheck.find(identifier)raise"Cannot find depcheck spec #{identifier.inspect}"if!depputs_header"Checking for #{dep.name}..."result=dep.checkresult={:found=>false}if!resultifresult[:found]&&!result[:error]puts_detail"Found: <green>yes</green>"elseifresult[:error]puts_detail"Found: #{result[:found]?"<yellow>yes, but there was an error</yellow>":"<red>no</red>"}"puts_detail"Error: <red>#{result[:error]}</red>"elseputs_detail"Found: #{result[:found]?"<green>yes</green>":"<red>no</red>"}"end@missing_dependencies<<dependresult.each_pairdo|key,value|ifkey.is_a?(String)puts_detail"#{key}: #{value}"endendendreturn@missing_dependencies.empty?ensurePlatformInfo.log_implementation=old_log_implendenddefprint_installation_instructions_for_missing_dependencies@missing_dependencies.eachdo|dep|puts" * To install <yellow>#{dep.name}</yellow>:"puts" #{dep.install_instructions}"ifdep.install_commentsputs" #{dep.install_comments}"endputsendendprivatedefputs(text=nil)iftext@stdout.puts(Utils::AnsiColors.ansi_colorize(text))else@stdout.putsend@stdout.flushenddefputs_header(text)puts" <b>* #{text}</b>"enddefputs_detail(text)puts" #{text}"endendend# module Depcheckend# module PlatformInfoend# module PhusionPassenger