Class: TT::CExtensionManager

Inherits:
Object
  • Object
show all
Defined in:
TT_Lib2/c_extension_manager.rb

Overview

Loads the appropriate C Extension loader after ensuring the appropriate version has been copied from the staging area.

Since:

  • 2.9.0

Constant Summary

VERSION_PATTERN =

Since:

  • 2.9.0

/\d+\.\d+\.\d+$/

Instance Method Summary collapse

Constructor Details

#initialize(path, version) ⇒ CExtensionManager

The `path` argument should point to the path where a 'stage' folder is located with the following folder structure:

+ `path`
+-+ stage
  +-+ 1.8
  | +-+ HelloWorld.so
  |   + HelloWorld.bundle
  +-+ 2.0
    +-+ HelloWorld.so
      + HelloWorld.bundle

The appropriate file will be copied on demand to a folder structure like: `path`/<EXTENSION_VERSION>/<RUBY_VERSION>/HelloWorld.so

When a new version is deployed the files will be copied again from the staging area to a new folder named with the new extension version.

The old versions are cleaned up if possible. This attempt is done upon each time #prepare_path is called.

This way the C extensions can be updated because they are never loaded from the staging folder directly.

Parameters:

  • path (String)

    The location where the C Extensions are located.

Since:

  • 2.9.0



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'TT_Lib2/c_extension_manager.rb', line 44

def initialize( path, version )
  # ENV, __FILE__, $LOAD_PATH, $LOADED_FEATURE and more might return an
  # encoding different from UTF-8. It's often ASCII-US or ASCII-8BIT.
  # If the developer has derived from these strings the encoding sticks with
  # it and will often lead to errors further down the road when trying to
  # load the files. To work around this the path is attempted to be
  # relabeled as UTF-8 if we can produce a valid UTF-8 string.
  # I'm forcing an encoding instead of converting because the encoding label
  # of the strings seem to be consistently mislabeled - the data is in
  # fact UTF-8.
  if path.respond_to?(:encoding)
    test_path = path.dup.force_encoding("UTF-8")
    path = test_path if test_path.valid_encoding?
  end

  unless version =~ VERSION_PATTERN
    raise ArgumentError, 'Version must be in "X.Y.Z" format'
  end
  unless File.directory?( path )
    raise IOError, "Stage path not found: #{path}"
  end

  @version = version
  @path = path
  @stage = File.join( path, 'stage' )
  @target = File.join( path, version )

  @log = []

  # See method comments for more info.
  #require_file_utils()
end

Instance Method Details

#prepare_pathString

Copies the necessary C Extension libraries to a version dependent folder from where they can be loaded. This will allow the SketchUp RBZ installer to update the extension without running into errors when trying to overwrite files from previous installation.

Returns:

  • (String)

    The path where the extensions are located.

Since:

  • 2.9.0



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'TT_Lib2/c_extension_manager.rb', line 84

def prepare_path
  log("prepare_path")

  pointer_size = ['a'].pack('P').size * 8 # 32 or 64
  ruby = RUBY_VERSION.split('.')[0..1].join('.') # Get Major.Minor string.
  platform = ( TT::System::PLATFORM_IS_OSX ) ? 'osx' : 'win'
  platform = "#{platform}#{pointer_size}"
  stage_path = File.join( @stage, ruby, platform )
  target_path = File.join( @target, ruby, platform )
  fallback = false

  log("> stage_path: #{stage_path}")
  log("> target_path: #{target_path}")

  begin
    # Copy files if target doesn't exist.
    unless File.directory?( stage_path )
      raise IOError, 'Staging directory not found'
    end
    unless File.directory?( target_path )
      log("MKDIR: #{target_path}")
      require_file_utils() # See method comments for more info.
      log(FileUtils.mkdir_p( target_path ))
    end
    stage_content = Dir.entries( stage_path )
    target_content = Dir.entries( target_path )
    log("> stage_content: #{stage_content}")
    log("> target_content: #{target_content}")
    unless (stage_content - target_content).empty?
      log("COPY: #{stage_path} => #{target_path}")
      require_file_utils() # See method comments for more info.
      log(FileUtils.copy_entry( stage_path, target_path ))
    end

    # Clean up old versions.
    version_pattern = /\d+\.\d+\.\d+$/
    filter = File.join( @path, '*' )
    log("> cleanup: #{filter}")
    Dir.glob( filter ).each { |entry|
      log(">>>   entry: #{entry}")
      next unless File.directory?( entry )
      log(">>> @target: #{@target} (#{entry.downcase == @target.downcase})")
      log(">>>  @stage: #{@stage} (#{entry.downcase == @stage.downcase})")
      log(">>>>> @target vs entry")
      log(">>>>> #{entry.class.name}: #{entry.bytes}") if entry.respond_to?(:bytes)
      log(">>>>> #{@target.class.name}: #{@target.bytes}") if @target.respond_to?(:bytes)
      next if entry.downcase == @stage.downcase || entry.downcase == @target.downcase
      log(">>>   match: #{entry =~ version_pattern}")
      next unless entry =~ version_pattern
      begin
        log("REMOVE: #{entry}")
        require_file_utils() # See method comments for more info.
        log(FileUtils.rm_r( entry ))
      rescue
        log_warn("#{TT::Lib::PLUGIN_NAME} - Unable to clean up: #{entry}")
      end
    }
  rescue Errno::EACCES
    if fallback
      UI.messagebox(
        "Failed to load #{TT::Lib::PLUGIN_NAME}. Missing permissions to " <<
        "Plugins and temp folder."
      )
      raise
    else
      # Even though the temp folder contains the username, it appear to be
      # returned in DOS 8.3 format which Ruby 1.8 can open. Fall back to
      # using the temp folder for these kind of systems.
      log_warn("#{TT::Lib::PLUGIN_NAME} - Unable to access: #{target_path}")
      temp_tt_lib_path = File.join( temp_path(), TT::Lib::PLUGIN_ID )
      target_path = File.join( temp_tt_lib_path, @version, ruby, platform )
      log_warn("#{TT::Lib::PLUGIN_NAME} - Falling back to: #{target_path}")
      fallback = true
      retry
    end
  end

  target_path
end

Since:

  • 2.10.7



173
174
175
# File 'TT_Lib2/c_extension_manager.rb', line 173

def print_log
  puts @log.join("\n")
end

#to_sString Also known as: inspect

Returns:

  • (String)

Since:

  • 2.9.0



166
167
168
169
# File 'TT_Lib2/c_extension_manager.rb', line 166

def to_s
  object_hex_id = "0x%x" % (self.object_id << 1)
  "<##{self.class}::#{object_hex_id}>"
end