Check that ffi-compiler loads before using it to define tasks. (#14538)

Fixes #14509

Closes #14538

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/14538 from JasonLunn:fix_14509 a3f9d0bd4f1f8036253c7a794a61e8cc84b4483c
PiperOrigin-RevId: 577837541
diff --git a/ruby/lib/google/tasks/ffi.rake b/ruby/lib/google/tasks/ffi.rake
index 00e665f..c7b2a8e 100644
--- a/ruby/lib/google/tasks/ffi.rake
+++ b/ruby/lib/google/tasks/ffi.rake
@@ -1,5 +1,3 @@
-require "ffi-compiler/compile_task"
-
 # # @param task [FFI::Compiler::CompileTask] task to configure
 def configure_common_compile_task(task)
   if FileUtils.pwd.include? 'ext'
@@ -49,46 +47,56 @@
   end
 end
 
-desc "Compile Protobuf library for FFI"
-namespace "ffi-protobuf" do
-  with_generated_files do
-    # Compile Ruby UPB separately in order to limit use of -DUPB_BUILD_API to one
-    # compilation unit.
-    desc "Compile UPB library for FFI"
-    namespace "ffi-upb" do
-      with_generated_files do
-        FFI::Compiler::CompileTask.new('ruby-upb') do |c|
-          configure_common_compile_task c
-          c.add_define "UPB_BUILD_API"
-          c.exclude << "/glue.c"
-          c.exclude << "/shared_message.c"
-          c.exclude << "/shared_convert.c"
-          if RbConfig::CONFIG['target_os'] =~ /darwin|linux/
-            c.cflags << "-fvisibility=hidden"
+begin
+  require "ffi-compiler/compile_task"
+
+  desc "Compile Protobuf library for FFI"
+  namespace "ffi-protobuf" do
+    with_generated_files do
+      # Compile Ruby UPB separately in order to limit use of -DUPB_BUILD_API to one
+      # compilation unit.
+      desc "Compile UPB library for FFI"
+      namespace "ffi-upb" do
+        with_generated_files do
+          FFI::Compiler::CompileTask.new('ruby-upb') do |c|
+            configure_common_compile_task c
+            c.add_define "UPB_BUILD_API"
+            c.exclude << "/glue.c"
+            c.exclude << "/shared_message.c"
+            c.exclude << "/shared_convert.c"
+            if RbConfig::CONFIG['target_os'] =~ /darwin|linux/
+              c.cflags << "-fvisibility=hidden"
+            end
           end
         end
       end
-    end
 
-    FFI::Compiler::CompileTask.new 'protobuf_c_ffi' do |c|
-      configure_common_compile_task c
-      # Ruby UPB was already compiled with different flags.
-      c.exclude << "/range2-neon.c"
-      c.exclude << "/range2-sse.c"
-      c.exclude << "/naive.c"
-      c.exclude << "/ruby-upb.c"
-    end
+      FFI::Compiler::CompileTask.new 'protobuf_c_ffi' do |c|
+        configure_common_compile_task c
+        # Ruby UPB was already compiled with different flags.
+        c.exclude << "/range2-neon.c"
+        c.exclude << "/range2-sse.c"
+        c.exclude << "/naive.c"
+        c.exclude << "/ruby-upb.c"
+      end
 
-    # Setup dependencies so that the .o files generated by building ffi-upb are
-    # available to link here.
-    # TODO Can this be simplified? Can the single shared library be used
-    # instead of the object files?
-    protobuf_c_task = Rake::Task[:default]
-    protobuf_c_shared_lib_task = Rake::Task[protobuf_c_task.prereqs.last]
-    ruby_upb_shared_lib_task = Rake::Task[:"ffi-upb:default"].prereqs.first
-    Rake::Task[ruby_upb_shared_lib_task].prereqs.each do |dependency|
-      protobuf_c_shared_lib_task.prereqs.prepend dependency
+      # Setup dependencies so that the .o files generated by building ffi-upb are
+      # available to link here.
+      # TODO Can this be simplified? Can the single shared library be used
+      # instead of the object files?
+      protobuf_c_task = Rake::Task[:default]
+      protobuf_c_shared_lib_task = Rake::Task[protobuf_c_task.prereqs.last]
+      ruby_upb_shared_lib_task = Rake::Task[:"ffi-upb:default"].prereqs.first
+      Rake::Task[ruby_upb_shared_lib_task].prereqs.each do |dependency|
+        protobuf_c_shared_lib_task.prereqs.prepend dependency
+      end
+    end
+  end
+rescue LoadError
+  desc "Compile Protobuf library for FFI"
+  namespace "ffi-protobuf" do
+    task :default do
+      warn "Skipping build of FFI; `gem install ffi-compiler` to enable."
     end
   end
 end
-