#!/usr/bin/env ruby =begin GridFlow Copyright (c) 2001-2011 by Mathieu Bouchard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See file ./COPYING for further informations on licensing terms. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =end require "rbconfig" CFLAGS = "" include RbConfig OSX = !!( ::RbConfig::CONFIG["arch"] =~ /darwin/ ) WIN = !!( ::RbConfig::CONFIG["arch"] =~ /(mingw|mswin)/ ) LOG = File.open "./config.log", "w" $cxx = "g++" LDSOFLAGS = [] FEATURES = {} DEFINES = {} OBJS = [] PDLIB = ["unicorn"] LDSO = {} $explicit=false # use ANSI / VT100 colours Red = "\e[0;1;31m" Green = "\e[0;1;32m" Light = "\e[0m" Dark = "\e[0;1;30m" Yellow = "\e[0;1;33;44m" White = "\e[0;1m" LOG.puts "-"*64, "Environment Variables: " ENV.each {|k,v| LOG.puts "#{k}=#{v}" } LOG.puts "-"*64 if not File.exist?("./configure") then puts "Run me from the right directory please."; exit 1 end begin Dir.mkdir "tmp"; rescue Errno::EEXIST; end #----------------------------------------------------------------# class Or attr_reader :a class<< self; alias [] new end def initialize(*a) @a=a end def to_s; @a.join " or "; end end def launch(*command) c=command.map{|x| x.gsub(/([!#$&*()\[\]{}`~\\\|;'"<>\?])/n, "\\\\\\1").gsub(/\n/,"\\n") }.join(" ") LOG.puts c IO.popen(c+" 2>&1","r") {|pipe| LOG.puts pipe.read } ret = $? ret = ret.to_i if RUBY_VERSION >= "1.9" ret = ret.to_int if RUBY_VERSION > "1.7" LOG.puts "process returned status \##{ret}\n" if ret>0 return ret<=0 end module Future; end class Feature $features = [] def self.add(*a,&b) $features << Feature.new(*a,&b) end def initialize(tag,name,&b) uses_so []; tag tag; name name instance_eval(&b) end def self.attr2(sym,&b) eval "def #{sym}(*args,&b) raise args.inspect if args.length>1 if b then @#{sym}=b.extend Future elsif args.length>0 then (@#{sym}=args[0]; self) else if Future===@#{sym} then @#{sym}.call else @#{sym} end end end" end attr2 :tag attr2 :name attr2 :produces_pdlib attr2 :status attr2 :uses_so attr2 :produces_o attr2 :uses_h attr2 :uses_feature attr2 :test attr2 :action def find_h name if name[0..0]=="/" then File.exist?(name) else C_INCLUDE_PATH.find {|x| File.exist?(x+"/"+name)} end end def c_test code, link=nil, flags=[] link = uses_so.dup if not link link=link.flatten ldlpath = ENV["LD_LIBRARY_PATH"] uses_h.each {|h| h[0..0] == "$" or find_h h or /framework/ =~ uses_so.join(" ") or raise "where is #{h} ?" } if uses_h ENV["LD_LIBRARY_PATH"] = ldlpath ? "./tmp:#{ldlpath}" : "./tmp" link[0,0]=LDSOFLAGS.find_all {|x| String===x and /^-L/ =~ x }.flatten code=code.gsub(/#include#/) { uses_h.map {|inc| "#include <#{inc}>\n" }.join"" } LOG.puts code name = "tmp/#{$$}" File.open(name+".cpp","w") {|f| f.puts code } command = [$cxx] + CFLAGS.split(/ /).reject{|x| x.length==0 } raise "gcc compilation error" if not launch *(command+flags+[name+".cpp","-o",name,*link.uniq]) name = "tmp\\#{$$}" if WIN raise "runtime error" if not launch name return true ensure ENV["LD_LIBRARY_PATH"] = ldlpath if ldlpath end def asm_test code, *link LOG.puts code name = "tmp/#{$$}" File.open(name+".asm","w") {|f| f.puts code } launch "nasm",name+".asm","-f",(if OSX then "macho" else "elf" end),"-o",name+".o" and launch $cxx,"-o",name,name+".o",*link and launch name end def try uses_so(uses_so[]) if Proc===uses_so if Or===uses_so k=1; uses_so.a.each {|i| r=dup.uses_so(i).name(name+" (try \##{k})").try; k+=1; return r if r } return false end uses_h(uses_h[]) if Proc===uses_h if Or===uses_h k=1; uses_h.a.each {|i| i=[i] if String===i; r=dup.uses_h(i) .name(name+ " <#{i[0]}>").try; k+=1; return r if r } return false end LOG.puts "", "-"*64 DUAL.print Yellow,"[#{tag}]" DUAL.print Light," "+name+": " arrow = "-"*([78-"#{tag}] #{name}: ".length,0].max)+ "> " (uses_feature||[]).find {|f| if not (if Or===f then f.a.find {|x| FEATURES[x] } else FEATURES[f] end) then DUAL.puts Red,arrow+"disabled (would need #{f})"; return end } if status==:disabled or ($explicit and test and not status==:always_check) then DUAL.puts Dark,arrow+"disabled"; return end fu = [fu] if not Array===fu for f in fu||[] do if FEATURES[f] then DUAL.puts Dark,arrow+"disabled (using #{f} instead)"; return end end if test begin tresult = test[self]; rescue StandardError => e; end if tresult then DUAL.puts Green,arrow+"found "+(if tresult!=true then " (#{tresult})" else "" end) else DUAL.puts Red,arrow+"missing "+(if e then "(#{e})" else "(return false)" end) LOG.puts e.inspect if e return false end else DUAL.puts Green,arrow+"enabled" end action[] if action FEATURES[tag] = self category = "main" category = produces_pdlib[0] if produces_pdlib (LDSO[category]||=[]).concat(uses_so).uniq! OBJS .concat(produces_o ||[]) PDLIB.concat(produces_pdlib||[]).uniq! true end end #----------------------------------------------------------------# def read_ld_so_conf return [] unless File.exist?("/etc/ld.so.conf") File.open("/etc/ld.so.conf"){|f| f.read }.split("\s").find_all {|line| line!="" } end def epath x; (ENV[x]||"").split(":") end C_INCLUDE_PATH = (epath("CPLUS_INCLUDE_PATH") + epath("C_INCLUDE_PATH") + ["/usr/include", "/usr/include/i386-linux-gnu"]).uniq LIBRARY_PATH = (epath("LIBRARY_PATH") + ["/usr/lib","/lib"]).uniq LD_LIBRARY_PATH = (epath("LD_LIBRARY_PATH") + read_ld_so_conf + ["/usr/lib","/lib"]).uniq LIBX11DIR = [ "-L/usr/X11R6/lib", "-L/opt/gnome/lib", "-L/usr/X11R6/lib64","-L/opt/gnome/lib64"] LIBX11 = LIBX11DIR + ["-lX11","-lXext"] def prepend_path base # so that people don't have to mess with env vars (why is CPLUS_INCLUDE_PATH not here ?) bl = base+"/lib" bi = base+"/include" if not LD_LIBRARY_PATH.include? bl and File.exist? bl then LD_LIBRARY_PATH.unshift bl end if not LIBRARY_PATH.include? bl and File.exist? bl then LIBRARY_PATH.unshift bl; LDSOFLAGS.unshift "-L"+bl end if not C_INCLUDE_PATH.include? bi and File.exist? bi then C_INCLUDE_PATH.unshift bi; CFLAGS << " -I"+bi end # why does this append instead of prepend ? end prepend_path "/sw" if OSX prepend_path "/usr/local" prepend_path ENV["HOME"] prepend_path "/Users/pddev/..." if OSX # mathieu's build on hans' farm #prepend_path "/usr/X11R6" # can we do thisĀ ? CFLAGS << " -I." C_INCLUDE_PATH.unshift "." for var in [:C_INCLUDE_PATH, :LIBRARY_PATH, :LD_LIBRARY_PATH] do LOG.puts "#{var}: #{eval(var.to_s).inspect}" end LOG.puts "-"*64 CFLAGS << " -I/usr/X11R6/include" C_INCLUDE_PATH.unshift "/usr/X11R6/include" #----------------------------------------------------------------# Feature.add("gcc3","GNU C++ Compiler 3 (or 4)") { status :always_check action proc { name = WIN ? "tmp\\#{$$}" : "tmp/#{$$}" DEFINES["GCC_VERSION"] = begin /GCC_VERSION\s+(.*)/.match(File.popen(name,"r"){|f| f.read })[1] rescue Exception; "3.666" # version number unknown, sorry. end } test proc {c_test %{ #include int main () { printf("GCC_VERSION %d.%d.%d\\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); return !(__GNUC__>=3); }}}} Feature.add("stl","libstdc++ (C++ Standard Template Library)") { status :always_check test proc {c_test %{ #include int main () {std::vector foo;}}}} Feature.add("gcc64","GNU C++ in 64-bit mode") { action proc {DEFINES["HAVE_GCC64"]=1} test proc {c_test %{ #include #include #include #include #define T(a) printf("%s:%ld; ",#a,(long)sizeof(a)); int main () { T(void *)T(ptrdiff_t)T(off_t)T(size_t)puts(""); T(char)T(short)T(int)T(long)T(long long)puts(""); T(float)T(double)puts(""); return sizeof(void*)!=8; }}}} Feature.add("pentium","Pentium-compatible CPU") { action proc { DEFINES["HAVE_PENTIUM"]=1 (::RbConfig::CONFIG["arch"] =~ /i\d86/) and DEFINES["CPU"] ||= "pentium" } test proc { (::RbConfig::CONFIG["arch"] =~ /(i\d86|x86_64|universal)/) or raise "#{::RbConfig::CONFIG["arch"]} instead" cflags = [] cflags << "-arch" << "i386" if OSX c_test '#define cpuid(func,ax,bx,cx,dx) \ __asm__ __volatile__("cpuid":"=a"(ax),"=b"(bx),"=c"(cx),"=d"(dx):"a"(func)) #include main() {int a[4]; cpuid(0,a[0],a[1],a[2],a[3]); fprintf(stderr,"cpuid: name=\"%.4s%.4s%.4s\", flags=0x%08x\n", (char *)&a[1],(char *)&a[3],(char *)&a[2],a[0]); return 0;}',nil,cflags}} Feature.add("mmx","MMX-compatible CPU in 32-bit mode with NASM") { uses_feature ["pentium"] produces_o ["src/mmx.o","src/mmx_loader.o"] test proc { _="" _="_" if OSX asm_test %{ global #{_}main extern #{_}exit align 16 SECTION .data foo: dd 42,0 SECTION .text #{_}main: lea esi,[foo] movq mm0,qword[esi] paddd mm0,qword[esi] movq qword [esi],mm0 emms cmp dword [foo], 84 je yes push long 1 call #{_}exit yes: push long 0 call #{_}exit}}} Feature.add("x11","X11 Display Protocol (including XSHM plugin)") { uses_so LIBX11 produces_pdlib ["x11"] uses_h ["X11/Xlib.h","sys/shm.h","X11/extensions/XShm.h"] test proc {c_test " #include# #include #include #include int main () {return XShmPutImage==0 || XSetErrorHandler==0;}"}} Feature.add("sdl","Simple Directmedia Layer (experimental support)") { uses_so {`sh -c 'sdl-config --libs' 2>&1`.split" "} # doesn't work if special chars in --libs; sh -c used because of WIN. uses_h ["SDL/SDL.h"] # should use $(sdl-config --cflags) with this produces_o ["src/sdl.o"] test proc {c_test " #include# #undef main int main () {return SDL_MapRGB==0;}"}} Feature.add("quartz","Apple Quartz/Cocoa Display") { uses_so ["-lobjc",["-framework","Cocoa"]] uses_h ["objc/Object.h","Cocoa/Cocoa.h"] produces_o ["src/quartz.o"] test proc {c_test " #include# int main () {return CGImageRelease==0;} ", nil, ["-xobjective-c++"]}} Feature.add("aalib","Ascii Art Library") { uses_so ["-laa"] uses_h ["aalib.h"] produces_o ["src/aalib.o"] test proc {c_test " #define aa_hardwareparams aa_hardware_params extern \"C\" { #include# }; int main () {return aa_init==0;}"}} Feature.add("jpeg","JPEG Library") { uses_so ["-ljpeg"] uses_h ["jpeglib.h"] produces_o ["src/jpeg.o"] test proc {c_test " extern \"C\" { #include #include# }; int main () {return jpeg_write_scanlines==0;}"}} Feature.add("png","PNG Library") { uses_so ["-lpng","-lz"] uses_h Or[["png.h"],["libpng/png.h"],["libpng14/png.h"],["libpng12/png.h"]] produces_o ["src/png.o"] test proc {|f| f.c_test %` extern "C" { #include #include# }; int main () { #define T(a) printf("%s:%ld; ",#a,long(sizeof(a))); T(png_uint_32)T(long)puts(""); if (!png_sig_cmp) return 1; return 0;}`}} Feature.add("libv4l1","Video4linux I Driver Interface Library") { produces_o ["src/videodev.o"] uses_h ["libv4l1.h"] uses_so ["-lv4l1"] test proc {c_test " #include #include int main () {return v4l1_open==0;}"}} Feature.add("libv4l2","Video4linux II Driver Interface Library") { uses_h ["libv4l2.h"] uses_so ["-lv4l2"] produces_o ["src/v4l2.o"] test proc {c_test " #include int main () {return v4l2_open==0;}"}} Feature.add("dc1394","DC1394 for Linux") { uses_so ["-ldc1394_control"] uses_h Or["libdc1394/dc1394_control.h","dc1394/control.h"] produces_o ["src/dc1394.o"] test proc {|f| f.c_test " #include# int main () {return dc1394_create_handle==0;}"}} Feature.add("mpeg3","HeroineWarrior LibMPEG3") { uses_so LIBX11DIR+["-lmpeg3","-lpthread","-lm"] uses_h Or["mpeg3/libmpeg3.h","libmpeg3/libmpeg3.h","libmpeg3.h"] produces_o ["src/mpeg3.o"] test proc {|f| f.c_test " #include# int main () {return mpeg3_open==0;}"}} Feature.add("quicktimeapple","Apple's QuickTime") { uses_so [["-framework","Quicktime"]] uses_h ["QuickTime/QuickTime.h","QuickTime/Movies.h"] produces_o ["src/quicktimeapple.o","src/quicktimecamera.o"] test proc {c_test " #include# int main () {return EnterMovies==0;}"}} Feature.add("quicktimehw","Plaum's LibQuickTime") { uses_so Or[ LIBX11DIR+["-lquicktime","-lpthread","-lpng","-ldl","-lglib-2.0","-lz"], LIBX11DIR+["-lquicktime","-lpthread","-lpng","-ldl","-lglib-1.2","-lz"], LIBX11DIR+["-lquicktime","-lpthread","-lpng","-ldl","-lglib" ,"-lz"]] f = ["quicktime.h","colormodels.h","lqt.h","lqt_version.h","lqt_codecinfo.h"] uses_h Or[ f.map{|x| "lqt/"+x }, f.map{|x| "quicktime/"+x }] produces_o ["src/quicktimehw.o"] test proc {|f| f.c_test %` #include #include# int main () { #ifdef LQT_VERSION printf("LQT_VERSION = %s\\n",LQT_VERSION); #else printf("LQT_VERSION = (undefined)"); #endif return quicktime_open==0; }`}} Feature.add("opencv","Intel OpenCV") { produces_o ["src/opencv.o"] uses_so `pkg-config --libs opencv`.split(/\s+/).reject {|x| # for some reason, pkg-config output is wrong on mathieu's system x=="-lhighgui" or x=="-lcvaux" or x=="-lopencv_contrib" } if not OSX #uses_so ["-lcv"] #uses_so ["-L/Users/pddev/lib","-lcv"] if OSX # for the build system used in 9.13 uses_so ["-F/Users/pddev/Frameworks -framework OpenCV"] if OSX #in `dirname': can't convert nil into String (TypeError) # I should figure how to make this interact with pkg-config properly #action proc {DEFINES["OPENCV_SHARE_PATH"] = File.dirname(C_INCLUDE_PATH.find {|x| File.exist?(x+"/opencv/cv.h")}) + "/share/opencv"} test proc {c_test %` #include int main () {return 0;}`}} Feature.add("fftw","FFTW (Fastest Fourier Transform in the West)") { produces_o ["src/fftw.o"] uses_so Or[["-lfftw3f","-lfftw3"],["-lfftw3f-3","-lfftw3-3"]] test proc {|f| f.c_test %` #include int main () {return 0;}`}} Feature.add("kinect","libfreenect") { produces_o ["src/kinect.o"] uses_so ["-lfreenect"] test proc {c_test %` #include int main () {return !freenect_set_led;} `} } if false Feature.add("opengl","OpenGL/GLEW (for GEM support and [gf/gl])") { produces_o ["src/opengl.o"] uses_so LIBX11DIR+["-lGLEW","-lGLU"] if not WIN uses_so ["-lglu32","-lopengl32","-lglew32"] if WIN uses_h ["GL/glew.h"] test proc {c_test %` #include# int main () {return (GLint)0;}`}} Feature.add("gem","GEM support (loadtime detect: don't disable)") { produces_pdlib ["gem_loader","gem9292","gem9293","gem9393"] uses_feature ["opengl"] uses_so LIBX11DIR+["-lGLEW","-lGLU"] if not WIN uses_so ["-lglu32","-lopengl32","-lglew32"] if WIN } Feature.add("pdp","PDP support (loadtime detect: don't disable)") { produces_pdlib ["pdp"] } #Feature.add("android","Android module") {produces_o ["src/android.o"]; test proc {raise "use --force-android if you want this"}} #--------------------------------# $features_h = {} $features.each {|f| $features_h[f.tag]=f } def usage log = "" log << "usage: ./configure " log << "[--use-compiler compiler] [--use-compiler-option option]* " log << "[--use-cpu cpu] [--lite] [--debug] [--explicit]" $features_h.keys.map {|k| k.to_s }.sort.each {|k| log << "[--no-#{k}] " } $features_h.keys.map {|k| k.to_s }.sort.each {|k| log << "[--force-#{k}] " } puts while log.length>0 do puts log.slice!(/^.{1,70} /) end end while ARGV.length>0 do arg=ARGV.shift case arg when /([^=]+)=(.*)/ ARGV.unshift $1,$2 when /^--no-(.*)/ ; name = $1.untaint if $features_h[name] then $features_h[name].status :disabled else puts "there is no feature called #{name}" end when /^--force-(.*)/; name = $1.untaint if $features_h[name] then $features_h[name].test nil else puts "there is no feature called #{name}" end when "--explicit"; $explicit=true when "--debug"; DEFINES["HAVE_DEBUG"]=1 when "--lite"; DEFINES["HAVE_LITE" ]=1 when "--help"; usage; exit 0 when "--use-compiler"; $cxx = ARGV.shift when "--use-compiler-option"; CFLAGS << " "+ARGV.shift when "--use-cpu"; DEFINES["CPU"] = ARGV.shift else puts "unknown option \"#{arg}\""; usage; exit 1 end end if DEFINES["HAVE_DEBUG"] then CFLAGS << " -O0 -fno-inline" else CFLAGS << " -O3" end #--------------------------------# DUAL = Object.new def DUAL.print color,x STDERR.print color if not WIN STDERR.print x; STDERR.flush LOG .print x; LOG .flush end def DUAL.puts(color,x) self.print color,x+"\n" end DUAL.puts White, "This is the GridFlow 9.14 configurator" $features.each {|feature| feature.try } LDSOFLAGS.uniq! $exit=false def need s if not FEATURES[s] then DUAL.puts Red,"You need to install #{$features_h[s].name}"; $exit=true end end need "gcc3" need "stl" (DUAL.puts Light,""; exit 1) if $exit #--------------------------------# LOG.puts "-"*64 LOG.puts "$cxx: #{$cxx.inspect}" LOG.puts "LDSOFLAGS: #{LDSOFLAGS.inspect}" LOG.puts "DEFINES: #{DEFINES.inspect}" LOG.puts "OBJS: #{OBJS.inspect}" LOG.puts "PDLIB: #{PDLIB.inspect}" for k in LDSO.keys.sort do LOG.puts "$LDSO[#{k.inspect}]: #{LDSO[k].inspect}" end LOG.puts "-"*64 DUAL.puts Light,"generating ./config.make and ./config.h" File.open("./config.make","w") {|f| f.puts "RUBY = "+::RbConfig::CONFIG["bindir"]+"/"+::RbConfig::CONFIG["RUBY_INSTALL_NAME"] if DEFINES["CPU"] CFLAGS << (if DEFINES["GCC_VERSION"] >= "4" then " -mtune=$(CPU)" else " -mcpu=$(CPU)" end) CFLAGS << " -march=$(CPU)" end CFLAGS << " -DMACOSX" if OSX f.puts "GFCFLAGS += " + CFLAGS f.puts "LDSOFLAGS += " + LDSOFLAGS.flatten.join(" ") for k,v in DEFINES do f.puts "#{k}=#{v}" end f.puts "CXX = "+$cxx f.puts "OBJS = "+OBJS.join(" ") f.puts "DLEXT = "+::RbConfig::CONFIG['DLEXT'] f.puts "PDLIB = gridflow$(PDSUF) "+PDLIB.map{|x|"gridflow_#{x}$(PDSUF)"}.join(" ") for k in LDSO.keys.sort do f.puts "LDSO_#{k} = "+LDSO[k].join(" ") if LDSO[k] end } File.open("config.h","w") {|f| f.puts " \#ifndef __CONFIG_H \#define __CONFIG_H /* this file was auto-generated by gridflow/configure */" f.puts "#define STARTUP_LIST(PRE) \\" f.puts OBJS.map {|o| "PRE startup_#{File.basename(o,'.o')}();"}.join("\\\n") for k,v in DEFINES do f.puts "\#define #{k} "+v.inspect end for tag in %w[dc1394 mpeg3 quicktimehw png] do next unless FEATURES[tag] f.puts "\#ifdef #{tag.upcase}_INCLUDE_HERE" f.puts "extern \"C\" {" for inc in FEATURES[tag].uses_h.to_a do f.puts "\#include <#{inc}>" end f.puts "};" f.puts "\#endif" end f.puts "\#endif /* __CONFIG_H */" } #--------------------------------# puts %{#{White}\ See ./config.log if you want the details of the configuration tests. "If you are satisfied with that configuration, you may go on, and do "make". there is no "make install" step : rename this folder to "gridflow", then move it to "lib/pd/extra" or "~/pd-externals" or add a -path option towards the parent folder (and not to the folder named "gridflow" itself). If you get stuck, ask gridflow-dev. #{Light}} END {system "/bin/rm -f tmp/#{$$} tmp/#{$$}.c tmp/#{$$}.o tmp/#{$$}.asm"}