## ## rocaml Copyright (c) 2007 Mauricio Fernandez ## http://eigenclass.org ## Use and redistribution subject to the same conditions as Ruby. ## See the LICENSE file included in rocaml's distribution for more ## information. require 'mkmf' %w[EXT_NAME OCAML_PACKAGES CAML_LIBS CAML_OBJS CAML_FLAGS CAML_INCLUDES].each do |c| begin c = Object.const_get(c) rescue Exception puts "You must define the #{c} constant" exit(1) end end #EXT_NAME = "foo" # extension name, XXX in require 'XXX' #OCAML_PACKAGES = %w[] # if non-empty, will use ocamlfind #CAML_LIBS = %w[] # some cmxa #CAML_OBJS = %w[] # list of .cmx, autodetected if empty #CAML_FLAGS = "" # compilation flags #CAML_INCLUDES = [] # -I options (-I must be prepended) CAML_TARGET = "#{EXT_NAME}_rocaml_runtime.o" if CAML_OBJS.empty? objects = Dir["*.ml"].map{|s| s.sub(/\.ml$/, ".cmx")}.reject do |f| f =~ /pa_.*/ end CAML_OBJS.replace(objects) end ocaml_native_lib_path = %w[ /usr/lib/ocaml/**/libasmrun.a /usr/local/lib/ocaml/**/libasmrun.a ].map{|glob| Dir[glob]}.flatten.sort.map{|x| File.dirname(x)}.last if ocaml_native_lib_path.nil? puts "Couldn't find OCaml's native code runtime libasmrun.a" exit end $INCFLAGS << " -I#{ocaml_native_lib_path}" maybe_opt = lambda{|x| opt = "#{x}.opt"; system(opt) ? opt : x } if OCAML_PACKAGES.empty? then OCAMLC = maybe_opt["ocamlc"] OCAMLOPT = maybe_opt["ocamlopt"] OCAMLDEP = maybe_opt["ocamldep"] else cmd = lambda{|x| "ocamlfind #{x} -package #{OCAML_PACKAGES.join(",")}"} OCAMLC = cmd["ocamlc"] OCAMLOPT = cmd["ocamlopt"] OCAMLDEP = cmd["ocamldep"] end def ocamlopt_ld_cmd(obj, *sources) linkpkg = OCAML_PACKAGES.empty? ? "" : "-linkpkg" "#{OCAMLOPT} $(OCAML_INCLUDES) -output-obj -o #{obj} $(OCAML_LIBS) #{sources.join(" ")} #{linkpkg}" end CAML_OBJS.push("rubyOCamlUtil.cmx") unless CAML_OBJS.include?("rubyOCamlUtil.cmx") File.open("rubyOCamlUtil.ml", "w") do |f| f.puts <&1`["version"].nil? camlp4version = `camlp4 -v 2>&1`[/version\s+(\d.*)/, 1] have_camlp4 = ! camlp4version.nil? pa_rocaml_revdeps = Dir["*.ml"].map do |f| "#{f.sub(/\.ml$/, ".cmx")}: pa_rocaml.cmo" end.join("\n") camlp4_flavor = nil if have_camlp5 CAML_FLAGS << " -pp 'camlp5o -I . pa_rocaml.cmo'" PA_ROCAML_RULES = <<-EOF pa_rocaml.cmo: pa_rocaml.ml ocamlc -c -I +camlp5 -pp "camlp5o -I +camlp5 pa_extend.cmo q_MLast.cmo -loc _loc" pa_rocaml.ml #{pa_rocaml_revdeps} EOF camlp4_flavor = 309 elsif have_camlp4 && camlp4version < "3.10.0" CAML_FLAGS << " -pp 'camlp4o -I . pa_rocaml.cmo'" PA_ROCAML_RULES = <<-EOF pa_rocaml.cmo: pa_rocaml.ml ocamlc -c -I +camlp4 -pp "camlp4o -I +camlp4 pa_extend.cmo q_MLast.cmo -loc _loc" pa_rocaml.ml #{pa_rocaml_revdeps} EOF camlp4_flavor = 309 elsif have_camlp4 && camlp4version >= "3.10.0" CAML_FLAGS << " -pp 'camlp4o -I . pa_rocaml.cmo'" PA_ROCAML_RULES = <<-EOF pa_rocaml.cmo: pa_rocaml.ml ocamlc -c -I +camlp4 -pp "camlp4orf -loc _loc" pa_rocaml.ml #{pa_rocaml_revdeps} EOF camlp4_flavor = 310 elsif defined? NEED_CAMLP4 && NEED_CAMLP4 puts <<-EOF This extension needs the camlp4 (or alternatively camlp5) Pre-Processor and Pretty Printer for OCaml, probably to be found in the camlp4 or camlp5 packages. camlp4 is distributed with OCaml and will be installed if you build the interpreter from the sources. Run extconf.rb again once you've installed it. EOF exit(1) else # not found, but not actually said to be needed either, assume it's OK PA_ROCAML_RULES = "" end if File.exist?("depend.in") File.open("depend", "w"){|f| f.print File.read("depend.in") } else File.open("depend", "w"){|f| } # overwrite previous end ocamldep_sources = (Dir["*.ml"] + Dir["*.mli"]) - %w[pa_rocaml.ml] File.open("depend", "a") do |f| f.puts < .depend include .depend EOF end PA_ROCAML_309 = <<'EOF' let module_name = let s = Sys.argv.(Array.length Sys.argv - 1) in String.capitalize (String.sub s 0 (String.rindex s '.')) let namespace = ref module_name let export _loc ids = let exprs = List.map (fun id -> let name = !namespace ^ "." ^ id in <:expr< Callback.register $str:name$ $lid:id$ >>) ids in <:str_item< do { $list:exprs$ } >> EXTEND Pcaml.str_item: LEVEL "top" [ [ "export"; names = LIST1 LIDENT SEP "," -> export _loc names | "export"; e = Pcaml.expr; "aliased"; id = Pcaml.expr -> let id = <:expr< $str:!namespace$ ^ "." ^ $id$ >> in <:str_item< do{ Callback.register $id$ $e$ } >> | "namespace"; n = STRING -> namespace := n; <:str_item< declare end >> ] ]; END;; EOF PA_ROCAML_310 = <<'EOF' open Camlp4.PreCast let module_name = let s = Sys.argv.(Array.length Sys.argv - 1) in String.capitalize (String.sub s 0 (String.rindex s '.')) let namespace = ref module_name let export _loc ids = let exprs = List.map (fun id -> let name = !namespace ^ "." ^ id in <:expr< Callback.register $str:name$ $lid:id$ >>) ids in <:str_item< do { $list:exprs$ } >> EXTEND Gram Syntax.str_item: LEVEL "top" [ [ "export"; names = LIST1 [ x = LIDENT -> x ] SEP "," -> export _loc names | "export"; e = Syntax.expr; "aliased"; id = Syntax.expr -> let id = <:expr< $str:!namespace$ ^ "." ^ $id$ >> in <:str_item< do{ Callback.register $id$ $e$ } >> | "namespace"; n = STRING -> namespace := n; <:str_item< >> ] ]; END;; EOF case camlp4_flavor when 309 File.open("pa_rocaml.ml", "w"){|f| f.puts PA_ROCAML_309} when 310 File.open("pa_rocaml.ml", "w"){|f| f.puts PA_ROCAML_310} end class Object old_create_makefile = instance_method(:create_makefile) define_method(:create_makefile) do |target| $LOCAL_LIBS = "#{CAML_TARGET} #{$LOCAL_LIBS}" old_create_makefile.bind(self).call(target) end end