Gem install problem in Sketchp 2021

When I am installing the google-protobuf gem then I am getting the following error in console.

ERROR: Failed to build gem native extension.
current directory: C:/Users/Ajay/AppData/Roaming/SketchUp/SketchUp 2021/SketchUp/Gems64/gems/google-protobuf-3.7.0/ext/google/protobuf_c

C:/Program\ Files/SketchUp/SketchUp\ 2021/Tools/bin/ruby.exe -I C:/Program\ Files/SketchUp/SketchUp\ 2021/Tools/RubyStdLib -r ./siteconf20220131-168-146xtyp.rb extconf.rb

extconf failedNo such file or directory - C:/Program Files/SketchUp/SketchUp 2021/Tools/bin/ruby.exe

Gem files will remain installed in C:/Users/Ajay/AppData/Roaming/SketchUp/SketchUp 2021/SketchUp/Gems64/gems/google-protobuf-3.7.0 for inspection.

Results logged to C:/Users/Ajay/AppData/Roaming/SketchUp/SketchUp 2021/SketchUp/Gems64/extensions/x64-mswin64-140/2.7.0/google-protobuf-3.7.0/gem_make.out'

using following source code

                begin
                    installer = Gem::DependencyInstaller.new();

                    if (version == nil) then
                        installer.install(name);
                    else
                        installer.install(name, version);
                    end
                rescue Exception => e
                    puts "Error in gem install :#{e}"
                    return false;
                end

Any help would be appreciated.

It is simple. You cannot build native (binary) gems inside SketchUp.

You would have to install a system Ruby and the Ruby DevKIt and build the gem external to SketchUp.
After the gem is built you would need to manually copy it to SketchUp’s gem folder.

But why does it work in SU2019 but not in SU2021?

I am sorry, but I do not believe that it could work in any version of SketchUp as it does not ship with a /bin folder nor ruby.exe.

Gems have never been fully supported by SketchUp.

Hi Dan,
You are right but I have tried the installation of gem in SU2019 on windows and mac platforms and it works there. How is SU2019 able to build but SU2021?

I refer you to the statements I made previously.

Because the pre-compiled version of google-protobuf is a mingw version, but newer versions of SU are Ruby mswin.

Note the folder in the error log you posted: x64-mswin64-140. That’s showing that RubyGems recognizes the Ruby build as mswin. RubyGems’ folder for mingw builds would be x64-mingw32.

Or, RubyGems will install a pre-compiled gem if the Ruby platform matches the gem’s platform. Otherwise, it will load the generic gem and attempt to build it. Since ‘building/compiling’ requires a stand-alone Ruby version, you would have to build the gem outside of SU, and also build the stand-alone Ruby, as mswin builds aren’t available.

Maybe Trimble would consider packaging standalone Ruby versions and having them available with a ‘absolutely no support’ policy?

2 Likes

We wouldn’t do that to enable gem workflow, because the gem workflow doesn’t work well within SketchUp with multiple extensions sharing the same environment.

Our stance is that if an extension needs a Ruby gem it needs to vendor it into the extension namespace in order for us to allow it on Extension Warehouse.

I want to use grpc gem in SU to transfer model data to cloud. GRPC is dependent on ‘google-protobuf’ and ‘googleapi’ gem. So, I have to build all the gems and embed them in our’s plugin namespace?

It is very cumbersome because some of the gems have a C extension.

Is there any easier way to solve this problem?

Yes. Because if any other extension was also to need the same gem it might depend on other versions and potentially clash.

The SketchUp Ruby environment is somewhat different than your typical Ruby web app, each extension (app) share the same environment and more considerations must be made to ensure the environment is stable to be shared.

Also one of the reasons why we don’t support gem usage. The gem model simply doesn’t fit well in an embedded desktop application environment.

Afraid not.

The answer might be a dedicated gem server specifically for SketchUp that enforces separate versioned installs where each version must be a mixin module that extensions must explicitly include into their namespace.

So for example the Google Protocol Buffer gem for general use is wrapped like …

module Google
  module Protobuf
    class Error < StandardError; end
    class ParseError < Error; end
    class TypeError < ::TypeError; end
    module Internal
      class AtomicCounter
      class Builder
      # ... etc ...
    end
    class DescriptorProto
    class RepeatedField
      # ... etc ...
  end
end

If these were implemented wrapped within:

module GemLib
  module Google
    module Protobuf
      module Version_3_19_4
         module Google
           module Protobuf
             # actaul gem version 3.19.4
           end
         end
      end
      module Version_4_0_0rc1
         # ... versioned gem modules ...
      end
      module Version_4_0_0rc2
         # ... versioned gem modules ...
      end
    end # Protobuf mixin lib
  end # Google mixin Lib
end # SketchUp specific Gem Lib

Then an extension would load it’s preferred version and mix it into the extension submodule …

module SomeAuthor
  module SomeExtension

    require "GemLib/Google/Protobuf/Version_3_19_4" # a .rb loader
    include "GemLib/Google/Protobuf/Version_3_19_4"
    # This would mixin the desired gem version into this
    # submodule as local constant references Google::Protobuf

  end # extension submodule
end # Author namespace module

Now a global convenience method like uselib could be implemented to make the loading and inclusion easier and more obvious.

The remaining challenge is how would gem libs use other specific version gem lib(s) as a dependancy.
We get into a Bundler like situation.

Rewrapping C Ruby gems is not trivial. The “google-protobuf” gem is nicely written with class and module definers that take a parent module parameter which can be simply changed at the bottom of the "protobuf.c" file. IE …

void Init_protobuf_c() {
  ObjectCache_Init();

  VALUE google = rb_define_module("Google");
  VALUE protobuf = rb_define_module_under(google, "Protobuf");

  Arena_register(protobuf);
  Defs_register(protobuf);
  RepeatedField_register(protobuf);
  Map_register(protobuf);
  Message_register(protobuf);

  cParseError = rb_const_get(protobuf, rb_intern("ParseError"));
  rb_gc_register_mark_object(cParseError);
  cTypeError = rb_const_get(protobuf, rb_intern("TypeError"));
  rb_gc_register_mark_object(cTypeError);

  rb_define_singleton_method(protobuf, "discard_unknown",
                             Google_Protobuf_discard_unknown, 1);
  rb_define_singleton_method(protobuf, "deep_copy",
                             Google_Protobuf_deep_copy, 1);
}

So in this case a couple of more wrapping modules can easily be defined.

But not all gem C code is written so nice as to be simply rewrapped.

I can say for sure that is not an effort we’ll be pursuing. That would be a lot of effort for a small subset of extensions.

Exactly, dependency resolution is very complicated. We’re not getting into the business for a small number of usecases.

Remember that there are multiple reasons we don’t support gems. Other reasons are that some gems require a compiler on the target (users) computer. Another is that installing a gem runs in the main thread and locks the UI for a significant time.

While somewhat cumbersome for the extension dev, this is one of them scenario where the extension dev has to accept the cost.

It might be that in some cases it’s worth investing the time to write your own Ruby C Extension that wraps a C/C++ library instead of trying to vendor a Ruby gem. That way it’s more of a one time effort and you wouldn’t have to redo all the work if you need to update the library.

I would argue that the current usage is small because the use / wrapping of gems is so complex.

If the SketchUp API had some kind of shared library mechanism, then many more extension authors would use them.

What I found very interesting was SpaceClaim’s C# API. They are released under a versioned namespace and extensions must explicitly use what ever version they need (likely have been tested under.)
So SC does not update / change previous versions. It adds a new one for each release.

That is why I envisioned a set of versions for each library, that can be shared.

Of course distributed libraries would need to be either pure Ruby or precompiled.

It is just ridiculous that everyone would have to repeat the same effort.

When you have C extension then the problem multiplies. You have to take care of

  • Platform - Mac/Windows
  • Architecture - 32/64 bit
  • Ruby version - SU supports different ruby versions.

Yea, there are other complications with Ruby C Extensions. I have a few extensions that use them. Though it’s mostly a one time cost of setup. Adding a new build target for a new version isn’t that much work.

All in all, there are no simple quick-fix solution for this. But relying on gems in and SketchUp extension is a no-go.