[Rubycocoa-devel 1355] Re: Submitting require_framework -- git 101

Back to archive index

Eloy Duran e.dur****@super*****
Sat Apr 26 04:38:42 JST 2008


Hi Scott,

Laurent asked me if I could send your patch to the list because he has  
some thoughts
and maybe others as well. So here it is (http://pasternak.superalloy.nl/pastes/335 
).

Cheers,
Eloy
diff --git a/framework/src/objc/BridgeSupport.m b/framework/src/objc/ 
BridgeSupport.m
index 13863b1..5b60665 100644
--- a/framework/src/objc/BridgeSupport.m
+++ b/framework/src/objc/BridgeSupport.m
@@ -26,6 +26,8 @@
  #import "ocexception.h"
  #import "objc_compat.h"

+#define BRIDGE_SUPPORT_NAME @"BridgeSupport"
+
  static VALUE cOSXBoxed;
  static ID ivarEncodingID;

@@ -2016,6 +2018,321 @@ osx_lookup_informal_protocol_method_type  
(VALUE rcv, VALUE sel,
    return method == NULL ? Qnil : rb_str_new2(method->encoding);
  }

+NSString *_find_framework_in_directory(NSString *base_path, NSString  
*framework_name)
+{
+  // Given a base directory, search for a Frameworks folder and a  
PrivateFrameworks
+  // folder, If a framework with a given name can be found in that  
folder, return
+  // the path to the framework.  If no such framework exists, then  
return nil.
+
+  // Make sure that the framework_name has an extension,  
add .framework if not
+  if([[framework_name pathExtension] length] == 0) {
+    framework_name = [framework_name stringByAppendingPathExtension:  
@"framework"];
+  }
+
+  NSString *frameworks_test_path = [[base_path  
stringByAppendingPathComponent: @"Frameworks"]  
stringByAppendingPathComponent: framework_name];
+  NSString *shared_frameworks_test_path = [[base_path  
stringByAppendingPathComponent: @"SharedFrameworks"]  
stringByAppendingPathComponent: framework_name];
+  NSString *private_frameworks_test_path = [[base_path  
stringByAppendingPathComponent: @"PrivateFrameworks"]  
stringByAppendingPathComponent: framework_name];
+
+  NSString *retVal = nil;
+  BOOL isDirectory = false;
+  NSFileManager *fileManager = [NSFileManager defaultManager];
+
+  if(!([fileManager fileExistsAtPath: private_frameworks_test_path  
isDirectory: &isDirectory] && isDirectory)) {
+
+    // The framework is not in private frameworks... try the public  
ones.
+    if(!([fileManager fileExistsAtPath: frameworks_test_path  
isDirectory: &isDirectory] && isDirectory)) {
+      // In documentation and on the lists, it looks like  
SharedFrameworks
+      // may be unused functionality in modern Mac OS X but we'll  
check it anyway
+      // for completeness
+      if(([fileManager fileExistsAtPath: shared_frameworks_test_path  
isDirectory: &isDirectory] && isDirectory)) {
+        retVal = shared_frameworks_test_path;
+      }
+    } else {
+      retVal = frameworks_test_path;
+    }
+  } else {
+    retVal = private_frameworks_test_path;
+  }
+
+  return retVal;
+}
+
+NSString *
+_find_shortcut_load_path(NSString *path_hint)
+{
+  // The system recognizes some shortcuts to frameworks that are  
buried in
+  // other umbrella frameworks.  This dictionary gives those shortcuts.
+  //
+  // Ideally external code would just load the umbrella frameworks,  
but this
+  // is retained for backward compatibility with existing code
+  NSDictionary *shortcuts = [NSDictionary dictionaryWithObjectsAndKeys:
+    @"/System/Library/Frameworks/ApplicationServices.framework/ 
Frameworks/CoreGraphics.framework", @"CoreGraphics",
+    @"/System/Library/Frameworks/Quartz.framework/Frameworks/ 
PDFKit.framework", @"PDFKit",
+    @"/System/Library/Frameworks/Quartz.framework/Frameworks/ 
QuartzComposer.framework", @"QuartzComposer",
+    @"/System/Library/Frameworks/Quartz.framework/Frameworks/ 
ImageKit.framework", @"ImageKit",
+    nil, nil];
+
+  return [shortcuts objectForKey: path_hint];
+}
+
+static NSString *nsstring_for_ruby_path(VALUE ruby_path)
+{
+  // Convert a ruby string into a NSString with a file system path.
+  Check_Type(ruby_path, T_STRING);
+  const char *path_cstr = RSTRING_PTR(ruby_path);
+  return [[NSFileManager defaultManager]  
stringWithFileSystemRepresentation: path_cstr
+                                                   length:  
strlen(path_cstr)];
+}
+
+NSString *
+_find_application_load_path(VALUE mOSX, NSString *path_hint)
+{
+  // When the init routines in RBRuntime.m are loading an  
appilcation, plugin
+  // or other bundle, they stuff the location of the frameworks and/or
+  // shared frameworks directory of that bundle in the array-valued  
constant
+  // RUBYCOCOA_FRAMEWORK_PATHS in the Ruby environment.  This routine  
searches
+  // those paths for a framework matching the given path hint.
+  NSString *framework_load_path = nil;
+
+  // Make sure that the path_hint has an extension, add .framework if  
not
+  if([[path_hint pathExtension] length] == 0) {
+    path_hint = [path_hint stringByAppendingPathExtension:  
@"framework"];
+  }
+
+  RB_ID constant_name = rb_intern("RUBYCOCOA_FRAMEWORK_PATHS");
+  if(rb_const_defined(mOSX, constant_name)) {
+    long index;
+    VALUE ruby_array = rb_const_get(mOSX, constant_name);
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+
+    Check_Type(ruby_array, T_ARRAY);
+    for(index = 0; (nil == framework_load_path) && index <  
RARRAY_LEN(ruby_array); index++)
+    {
+      VALUE ruby_search_path = rb_ary_entry(ruby_array, index);
+      Check_Type(ruby_search_path, T_STRING);
+
+      BOOL isDirectory = false;
+      NSString *search_path = nsstring_for_ruby_path(ruby_search_path);
+      NSString *framework_test_path = [search_path  
stringByAppendingPathComponent: path_hint];
+      if([fileManager fileExistsAtPath: framework_test_path  
isDirectory: &isDirectory] && isDirectory) {
+        framework_load_path = framework_test_path;
+      }
+    }
+  }
+
+  return framework_load_path;
+}
+
+NSString *
+_bundle_path_for_framework(VALUE mOSX, NSString *path_hint)
+{
+  NSString *framework_load_path = nil; // The result we are looking  
for.
+
+  BOOL isDirectory = false;
+  if([path_hint isAbsolutePath]
+        && [[NSFileManager defaultManager] fileExistsAtPath:  
path_hint isDirectory: &isDirectory]
+        && isDirectory)
+  {
+    // If the path is an absolute path to a directory, then we assume  
its a
+    // framework and return it.
+    framework_load_path = path_hint;
+  } else {
+    // If the path hint is one of the shortcut paths, this will  
resolve the
+    // shortcut and return the proper path.
+    framework_load_path = _find_shortcut_load_path(path_hint);
+
+    // If it's not found yet, consult the application load paths that
+    // are part of the Ruby environment.
+    if(nil == framework_load_path) {
+      framework_load_path = _find_application_load_path(mOSX,  
path_hint);
+    }
+
+    // Still not found? Try the standard library locations
+    if(nil == framework_load_path) {
+      NSString *search_path = nil;
+      NSArray *paths_to_search =  
NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,  
NSUserDomainMask | NSLocalDomainMask | NSSystemDomainMask, true);
+      NSEnumerator *enumerator = [paths_to_search objectEnumerator];
+      while( (nil == framework_load_path) && nil != (search_path =  
[enumerator nextObject]))
+      {
+        framework_load_path =  
_find_framework_in_directory(search_path, path_hint);
+      }
+    }
+
+    // If the framework still hasn't been found then we look inside the
+    // Developer tree for it.
+    if(nil == framework_load_path)
+    {
+      NSString *search_path = nil;
+      NSArray *paths_to_search =  
NSSearchPathForDirectoriesInDomains(NSDeveloperDirectory,  
NSUserDomainMask | NSLocalDomainMask | NSSystemDomainMask, YES);
+      NSEnumerator *enumerator = [paths_to_search objectEnumerator];
+      while( (nil == framework_load_path) && nil != (search_path =  
[enumerator nextObject]))
+      {
+        framework_load_path =  
_find_framework_in_directory(search_path, path_hint);
+      }
+    }
+  }
+
+  return framework_load_path;
+}
+
+static VALUE
+osx__bundle_path_for_framework(VALUE mOSX, VALUE ruby_path_hint)
+{
+  // This is a Ruby interface routine for _bundle_path_for_framework.
+  // It does little more than handle translation of the arguments and
+  // return values.
+
+  VALUE retVal = Qnil;
+
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+  NSString *path_hint = nsstring_for_ruby_path(ruby_path_hint);
+  NSString *bundle_path = _bundle_path_for_framework(mOSX, path_hint);
+  if(bundle_path)
+  {
+    const char *export_path = [bundle_path fileSystemRepresentation];
+    retVal = rb_str_new2(export_path);
+  }
+
+  [pool release];
+  return retVal;
+}
+
+// Forward declaration of _load_subframeworks for recursive calling
+static void _load_subframeworks(VALUE mOSX, NSBundle  
*umbrella_framework);
+
+static VALUE _load_framework(VALUE mOSX, NSString *framework_load_path)
+{
+    VALUE retVal = Qfalse;
+
+    // Load a given framework and import its bridge support symbols
+    const char *path_as_cstr = [framework_load_path  
fileSystemRepresentation];
+
+    NSBundle *framework_as_bundle = [NSBundle bundleWithPath:  
framework_load_path];
+    if(nil != framework_as_bundle)
+    {
+
+      // If the framework has already been loaded then  
require_framework returns false.
+      // But we go ahead and try to read the bridge support  
information.
+      if([framework_as_bundle isLoaded]) {
+        retVal = Qfalse;
+      } else {
+        // The framework was not loaded so try to load it.
+        NSError *error = nil;
+        if(![framework_as_bundle loadAndReturnError: &error])
+        {
+          rb_raise(rb_eRuntimeError, "Framework at path `%s' could  
not be loaded: %s",
+            path_as_cstr, [[error description] UTF8String]);
+        } else {
+          retVal = Qtrue;
+        }
+      }
+
+      rb_funcall(mOSX, rb_intern("load_bridge_support_signatures"),  
1, rb_str_new2(path_as_cstr));
+
+      // Load the contents of subframeworks as well.
+      _load_subframeworks(mOSX, framework_as_bundle);
+    } else {
+      rb_raise(rb_eRuntimeError, "The directory at path `%s' is not a  
framework or could not be loaded", path_as_cstr);
+    }
+
+    return retVal;
+}
+
+static void _load_subframeworks_in_path(VALUE mOSX, NSString  
*base_path)
+{
+  // Given a base path that is assumed to be a folder, find all the  
framework
+  // items in that path and try to load them.  This can end up  
recursively
+  // calling this routine.
+  NSError *file_error = nil;
+
+  NSArray *items_in_base = [[NSFileManager defaultManager]  
contentsOfDirectoryAtPath: base_path error: &file_error];
+  if( nil == file_error && nil != items_in_base)
+  {
+    NSString *path_to_load = nil;
+    NSEnumerator *file_enumerator = [items_in_base objectEnumerator];
+    while(nil != (path_to_load = [file_enumerator nextObject]))
+    {
+      if([[path_to_load pathExtension] isEqualToString: @"framework"])
+      {
+        _load_framework(mOSX, [base_path  
stringByAppendingPathComponent: path_to_load]);
+      }
+    }
+  }
+}
+
+static void _load_subframeworks(VALUE mOSX, NSBundle  
*umbrella_framework)
+{
+  // Load both the shared and private frameworks of the umbrella  
framework
+  // (if any)
+  _load_subframeworks_in_path(mOSX, [umbrella_framework  
privateFrameworksPath]);
+  _load_subframeworks_in_path(mOSX, [umbrella_framework  
sharedFrameworksPath]);
+}
+
+static VALUE
+osx_require_framework(VALUE mOSX, VALUE ruby_path_hint)
+{
+  VALUE retVal = Qfalse;
+
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+  // Try to find the path to a framework given the hint.  If one is  
found
+  // try to load it.
+  NSString *path_hint = nsstring_for_ruby_path(ruby_path_hint);
+  NSString *framework_load_path = _bundle_path_for_framework(mOSX,  
path_hint);
+  if(framework_load_path)
+  {
+    retVal = _load_framework(mOSX, framework_load_path);
+  } else {
+    [pool release];
+    pool = nil;
+
+    // Note, this raises an ArgumntError to be consistent with the  
previous RubyCocoa code, however
+    // in MacRuby the code that performs this same function raises a  
RuntimeError.
+  	rb_raise(rb_eArgError, "framework `%s' could not be found",  
RSTRING_PTR(ruby_path_hint));
+  }
+
+  [pool release];
+  return retVal;
+}
+
+static VALUE
+osx_get_bridgesupport_search_paths(VALUE mOSX)
+{
+  VALUE pathsArray = rb_ary_new();
+
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+  // Get the list of the usual domain search paths and append  
BridgeSupport to
+  // them construct a ruby array along the way.  These paths will be  
used by the
+  // Ruby side of things to locate bridge support files.
+  NSArray *searchPaths =  
NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,  
NSUserDomainMask | NSLocalDomainMask | NSSystemDomainMask, true);
+  NSEnumerator *enumerator = [searchPaths objectEnumerator];
+  NSString *pathToAdd;
+
+  while(nil != (pathToAdd = [enumerator nextObject]))
+  {
+    NSString *completePath = [pathToAdd  
stringByAppendingPathComponent: BRIDGE_SUPPORT_NAME];
+    rb_ary_push(pathsArray, rb_str_new2([completePath  
fileSystemRepresentation]));
+  }
+
+  // When the initialization routines in RBRuntime.m initialize  
RubyCocoa for
+  // an applicaiton, or other type of bundle, they put the  
application's private
+  // bridge support directories in a Ruby constant named  
RUBYCOCOA_SIGN_PATHS.
+  // We prefix the system search paths in the pathsArray with the  
values from
+  // RUBYCOCOA_SIGN_PATHS if it is defined.
+  RB_ID constant_name = rb_intern("RUBYCOCOA_SIGN_PATHS");
+  if(rb_const_defined(mOSX, constant_name)) {
+    VALUE app_paths_array = rb_const_get(mOSX, constant_name);
+    Check_Type(app_paths_array, T_ARRAY);
+
+    pathsArray = rb_ary_plus(pathsArray, app_paths_array);
+  }
+
+  [pool release];
+
+  return pathsArray;
+}
+
  void
  initialize_bridge_support (VALUE mOSX)
  {
@@ -2047,4 +2364,13 @@ initialize_bridge_support (VALUE mOSX)

    rb_define_module_function(mOSX,  
"lookup_informal_protocol_method_type",
      osx_lookup_informal_protocol_method_type, 2);
+
+  rb_define_module_function(mOSX, "require_framework",
+    osx_require_framework, 1);
+
+  rb_define_module_function(mOSX, "get_bridgesupport_search_paths",
+    osx_get_bridgesupport_search_paths, 0);
+
+  rb_define_module_function(mOSX, "_bundle_path_for_framework",
+    osx__bundle_path_for_framework, 1);
  }
diff --git a/framework/src/ruby/osx/objc/oc_import.rb b/framework/src/ 
ruby/osx/objc/oc_import.rb
index 78229d1..cff1b71 100644
--- a/framework/src/ruby/osx/objc/oc_import.rb
+++ b/framework/src/ruby/osx/objc/oc_import.rb
@@ -8,94 +8,9 @@
  require 'osx/objc/oc_wrapper'

  module OSX
-
-  FRAMEWORK_PATHS = [
-    '/System/Library/Frameworks',
-    '/Library/Frameworks'
-  ]
-
-  SIGN_PATHS = [
-    '/System/Library/BridgeSupport',
-    '/Library/BridgeSupport'
-  ]
-
-  PRE_SIGN_PATHS =
-    if path = ENV['BRIDGE_SUPPORT_PATH']
-      path.split(':')
-    else
-      []
-    end
-
-  FRAMEWORK_PATHS.concat(RUBYCOCOA_FRAMEWORK_PATHS)
-
-  if path = ENV['HOME']
-    FRAMEWORK_PATHS << File.join(ENV['HOME'], 'Library', 'Frameworks')
-    SIGN_PATHS << File.join(ENV['HOME'], 'Library', 'BridgeSupport')
-  end
-
-  # A name-to-path cache for the frameworks we support that are  
buried into umbrella frameworks.
-  QUICK_FRAMEWORKS = {
-    'CoreGraphics' => '/System/Library/Frameworks/ 
ApplicationServices.framework/Frameworks/CoreGraphics.framework',
-    'PDFKit' => '/System/Library/Frameworks/Quartz.framework/ 
Frameworks/PDFKit.framework',
-    'QuartzComposer' => '/System/Library/Frameworks/Quartz.framework/ 
Frameworks/QuartzComposer.framework',
-    'ImageKit' => '/System/Library/Frameworks/Quartz.framework/ 
Frameworks/ImageKit.framework'
-  }
-
-  def _bundle_path_for_framework(framework)
-    if framework[0] == ?/
-      [OSX::NSBundle.bundleWithPath(framework), framework]
-    elsif path = QUICK_FRAMEWORKS[framework]
-      [OSX::NSBundle.bundleWithPath(path), path]
-    else
-      path = FRAMEWORK_PATHS.map { |dir|
-        File.join(dir, "#{framework}.framework")
-      }.find { |path|
-        File.exist?(path)
-      }
-      if path
-        [OSX::NSBundle.bundleWithPath(path), path]
-      end
-    end
-  end
-  module_function :_bundle_path_for_framework
-
-  # The OSX::require_framework method imports Mac OS X frameworks and  
uses the
-  # BridgeSupport metadata to add Ruby entry points for the  
framework's Classes,
-  # methods, and Constants into the OSX module.
-  #
-  # The framework parameter is a reference to the framework that  
should be
-  # imported.  This may be a full path name to a particular  
framework, a shortcut,
-  # or a framework name.  The shortcuts are the keys listed in the
-  # <tt>QUICK_FRAMEWORKS</tt> hash.
-  #
-  # If a framework name (with no path) is given, then the method  
searches a number
-  # of directories.  Those directories (in search order) are:
-  #   1.  /System/Library/Frameworks
-  #   2.  /Library/Frameworks
-  #   3.  Any directories in the RUBYCOCOA_FRAMEWORK_PATHS array, if  
defined
-  #   4.  ENV['HOME']/Library/Frameworks, if the HOME environment  
variable is defined
-  #
-  # When using the search paths, the <tt>.framework</tt> file type  
extension should
-  # be omitted from the framework name passed to the method.
-  #
-  # If the method loads the framework successfully, it returns  
<tt>true</tt>.
-  # If the framework was already loaded the method returns <tt>false</ 
tt>.
-  # If the method is unable to locate, or unable to load the  
framework then it
-  # raises an <tt>ArgumentError</tt>.
-  def require_framework(framework)
-    return false if framework_loaded?(framework)
-    bundle, path = _bundle_path_for_framework(framework)
-    bundle.oc_load
-    if not bundle.isLoaded? then
-      raise ArgumentError, "Can't load framework '#{framework}'"
-    end
-    load_bridge_support_signatures(path)
-    return true
-  end
-  module_function :require_framework
-
    def framework_loaded?(framework)
-    bundle, path = _bundle_path_for_framework(framework)
+    path = _bundle_path_for_framework(framework)
+    bundle = NSBundle.bundleWithPath(path);
      unless bundle.nil?
        loaded = bundle.isLoaded
        if loaded then
@@ -135,35 +50,28 @@ module OSX
    module_function :__load_bridge_support_file__

    def load_bridge_support_signatures(framework)
-    # First, look into the pre paths.
-    fname = framework[0] == ?/ ? File.basename(framework,  
'.framework') : framework
-    PRE_SIGN_PATHS.each { |dir| return true if  
__load_bridge_support_file__(dir, fname) }
-
-    # A path to a framework, let's search for a BridgeSupport file  
inside the Resources folder.
-    if framework[0] == ?/
-      path = File.join(framework, 'Resources', 'BridgeSupport')
+    # strip the framework path down to it's base file name.
+    fname = (framework[0] == ?/) ? File.basename(framework,  
'.framework') : framework
+
+    # The environment variable BRIDGE_SUPPORT_PATH can contain a list  
of
+    # directories that should be searched for bridge support files  
before the
+    # standard loctions are examined.
+    bridge_support_env = ENV['BRIDGE_SUPPORT_PATH'];
+    bridge_support_paths = bridge_support_env ?  
bridge_support_env.split(':') : []
+    bridge_support_paths.each { |dir| return true if  
__load_bridge_support_file__(dir, fname) }
+
+    # search the framework itself to see if it has BridgeSupport in its
+    # resources area
+    path = _bundle_path_for_framework(framework);
+    if path && File.exist?(path)
+      path = File.join(path, 'Resources', 'BridgeSupport')
        return true if __load_bridge_support_file__(path, fname)
-      framework = fname
-    end
-
-    # Let's try to localize the framework and see if it contains the  
metadata.
-    FRAMEWORK_PATHS.each do |dir|
-      path = File.join(dir, "#{framework}.framework")
-      if File.exist?(path)
-        path = File.join(path, 'Resources', 'BridgeSupport')
-        return true if __load_bridge_support_file__(path, fname)
-      end
-    end
-
-    # Try the app/bundle specific and RubyCocoa.framework metadata  
directories.
-    RUBYCOCOA_SIGN_PATHS.each do |path|
-      if File.exist?(path) then
-        return true if __load_bridge_support_file__(path, fname)
-      end
      end

-    # We can still look into the general metadata directories.
-    SIGN_PATHS.each { |dir| return true if  
__load_bridge_support_file__(dir, fname) }
+    # Get the bridge support folders in the currently loading bundle,  
as well
+    # as those in the file domain areas (~/Library/BridgeSupport, / 
Library/BridgeSupport
+    # /System/Library/BridgeSupport, etc).
+    get_bridgesupport_search_paths.each { |dir| return true if  
__load_bridge_support_file__(dir, fname) }

      # Damnit!
      warn "Can't find signatures file for #{framework}" if OSX._debug?


On 19 apr 2008, at 05:11, Scott Thompson wrote:
> My apologies for needing remedial hand-holding on this.  I'm new to
> git and it's got me a bit confused.
>
> I've made my changes in support of require_framework and I'd like to
> submit them now.  I'm afraid, however, that after reading a lot of
> documentation on git, I'm not sure how to do that.
>
> I used "git" to clone down a copy of the source.  Then I branched the
> code, made my changes in the branch, but I'm unsure how to proceed.
> My git status says something like:
>
> # On branch rst_require_framework
> # Changes to be committed:
> #   (use "git reset HEAD <file>..." to unstage)
> #
> #	modified:   framework/src/objc/BridgeSupport.m
> #	modified:   framework/src/ruby/osx/objc/oc_import.rb
> #
> # Changed but not updated:
> #   (use "git add <file>..." to update what will be committed)
> #
> #	modified:   framework/RubyCocoa.xcodeproj/project.pbxproj
> #
> [snip -- untracked files left off for brevity]
>
> So the two files I've changed are there in my branch and I'm ready to
> commit them.  What's not clear to me is how I make those changes
> available to you guys so you can review them and put them in the
> "real" repository.
>
> My understanding is that when I did the git clone, I got both a
> repository and a working copy.  If I commit it should change the
> repository, but how do I forward those changes to the community at
> large?
>
> Scott
>
> _______________________________________________
> Rubycocoa-devel mailing list
> Rubyc****@lists*****
> http://lists.sourceforge.jp/mailman/listinfo/rubycocoa-devel




More information about the Rubycocoa-devel mailing list
Back to archive index