• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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