1# # @param task [FFI::Compiler::CompileTask] task to configure 2def configure_common_compile_task(task) 3 if FileUtils.pwd.include? 'ext' 4 src_dir = '.' 5 third_party_path = 'third_party/utf8_range' 6 else 7 src_dir = 'ext/google/protobuf_c' 8 third_party_path = 'ext/google/protobuf_c/third_party/utf8_range' 9 end 10 11 task.add_include_path third_party_path 12 task.add_define 'NDEBUG' 13 task.cflags << "-std=gnu99 -O3" 14 [ 15 :convert, :defs, :map, :message, :protobuf, :repeated_field, :wrap_memcpy 16 ].each { |file| task.exclude << "/#{file}.c" } 17 task.ext_dir = src_dir 18 task.source_dirs = [src_dir] 19 if RbConfig::CONFIG['target_os'] =~ /darwin|linux/ 20 task.cflags << "-Wall -Wsign-compare -Wno-declaration-after-statement" 21 end 22end 23 24# FFI::CompilerTask's constructor walks the filesystem at task definition time 25# to create subtasks for each source file, so files from third_party must be 26# copied into place before the task is defined for it to work correctly. 27# TODO Is there a sane way to check for generated protos under lib too? 28def with_generated_files 29 expected_path = FileUtils.pwd.include?('ext') ? 'third_party/utf8_range' : 'ext/google/protobuf_c/third_party/utf8_range' 30 if File.directory?(expected_path) 31 yield 32 else 33 task :default do 34 # It is possible, especially in cases like the first invocation of 35 # `rake test` following `rake clean` or a fresh checkout that the 36 # `copy_third_party` task has been executed since initial task definition. 37 # If so, run the task definition block now and invoke it explicitly. 38 if File.directory?(expected_path) 39 yield 40 Rake::Task[:default].invoke 41 else 42 raise "Missing directory #{File.absolute_path(expected_path)}." + 43 " Did you forget to run `rake copy_third_party` before building" + 44 " native extensions?" 45 end 46 end 47 end 48end 49 50begin 51 require "ffi-compiler/compile_task" 52 53 desc "Compile Protobuf library for FFI" 54 namespace "ffi-protobuf" do 55 with_generated_files do 56 # Compile Ruby UPB separately in order to limit use of -DUPB_BUILD_API to one 57 # compilation unit. 58 desc "Compile UPB library for FFI" 59 namespace "ffi-upb" do 60 with_generated_files do 61 FFI::Compiler::CompileTask.new('ruby-upb') do |c| 62 configure_common_compile_task c 63 c.add_define "UPB_BUILD_API" 64 c.exclude << "/glue.c" 65 c.exclude << "/shared_message.c" 66 c.exclude << "/shared_convert.c" 67 if RbConfig::CONFIG['target_os'] =~ /darwin|linux/ 68 c.cflags << "-fvisibility=hidden" 69 end 70 end 71 end 72 end 73 74 FFI::Compiler::CompileTask.new 'protobuf_c_ffi' do |c| 75 configure_common_compile_task c 76 # Ruby UPB was already compiled with different flags. 77 c.exclude << "/utf8_range.c" 78 c.exclude << "/ruby-upb.c" 79 end 80 81 # Setup dependencies so that the .o files generated by building ffi-upb are 82 # available to link here. 83 # TODO Can this be simplified? Can the single shared library be used 84 # instead of the object files? 85 protobuf_c_task = Rake::Task[:default] 86 protobuf_c_shared_lib_task = Rake::Task[protobuf_c_task.prereqs.last] 87 ruby_upb_shared_lib_task = Rake::Task[:"ffi-upb:default"].prereqs.first 88 Rake::Task[ruby_upb_shared_lib_task].prereqs.each do |dependency| 89 protobuf_c_shared_lib_task.prereqs.prepend dependency 90 end 91 end 92 end 93rescue LoadError 94 desc "Compile Protobuf library for FFI" 95 namespace "ffi-protobuf" do 96 task :default do 97 warn "Skipping build of FFI; `gem install ffi-compiler` to enable." 98 end 99 end 100end 101