Permalink
Browse files

spec UI + static libraries support

  • Loading branch information...
1 parent 8fac457 commit 5a37c3df38b7e3a759b0897e6c1774a7ed83f488 @lrz lrz committed Jul 5, 2012
Showing with 699 additions and 39 deletions.
  1. +11 −1 lib/motion/project.rb
  2. +2 −2 lib/motion/project/app.rb
  3. +104 −21 lib/motion/project/builder.rb
  4. +30 −3 lib/motion/project/config.rb
  5. +8 −12 lib/motion/spec.rb
  6. +544 −0 lib/motion/spec/helpers/ui.rb
View
@@ -134,7 +134,7 @@ def create_ipa
end
end
-desc "Run specs"
+desc "Run the test/spec suite"
task :spec do
App.config.spec_mode = true
Rake::Task["simulator"].invoke
@@ -182,6 +182,16 @@ def create_ipa
end
end
+desc "Create a .a static library"
+task :static do
+ libs = %w{iPhoneSimulator iPhoneOS}.map do |platform|
+ '"' + App.build(platform, :static => true) + '"'
+ end
+ fat_lib = File.join(App.config.build_dir, App.config.name + '-universal.a')
+ App.info 'Create', fat_lib
+ sh "/usr/bin/lipo -create #{libs.join(' ')} -output \"#{fat_lib}\""
+end
+
=begin
# Automatically load project extensions. A project extension is a gem whose
# name starts with `motion-' and which exposes a `lib/motion/project' libdir.
@@ -64,8 +64,8 @@ def setup
config.validate
end
- def build(platform)
- builder.build(config, platform)
+ def build(platform, opts={})
+ builder.build(config, platform, opts)
end
def archive
@@ -27,10 +27,12 @@ module Motion; module Project;
class Builder
include Rake::DSL if Rake.const_defined?(:DSL)
- def build(config, platform)
+ def build(config, platform, opts)
datadir = config.datadir
archs = config.archs[platform]
+ static_library = opts.delete(:static)
+
ruby = File.join(config.bindir, 'ruby')
llc = File.join(config.bindir, 'llc')
@@ -160,15 +162,15 @@ def build(config, platform)
FileUtils.touch(objs_build_dir) if project_file_changed
app_objs = objs
+ spec_objs = []
if config.spec_mode
# Build spec files too, but sequentially.
- objs << build_file.call(File.expand_path(File.join(File.dirname(__FILE__), '../spec.rb')))
spec_objs = config.spec_files.map { |path| build_file.call(path) }
objs += spec_objs
end
- # Generate main file.
- main_txt = <<EOS
+ # Generate init file.
+ init_txt = <<EOS
#import <UIKit/UIKit.h>
extern "C" {
@@ -184,8 +186,72 @@ def build(config, platform)
void rb_rb2oc_exc_handler(void);
void rb_exit(int);
EOS
- objs.each do |_, init_func|
- main_txt << "void #{init_func}(void *, void *);\n"
+ app_objs.each do |_, init_func|
+ init_txt << "void #{init_func}(void *, void *);\n"
+ end
+ init_txt << <<EOS
+}
+
+extern "C"
+void
+RubyMotionInit(int argc, char **argv)
+{
+ static bool initialized = false;
+ if (!initialized) {
+ ruby_init();
+ ruby_init_loadpath();
+ if (argc > 0) {
+ const char *progname = argv[0];
+ ruby_script(progname);
+ }
+ try {
+ void *self = rb_vm_top_self();
+EOS
+ app_objs.each do |_, init_func|
+ init_txt << "#{init_func}(self, 0);\n"
+ end
+ init_txt << <<EOS
+ }
+ catch (...) {
+ rb_rb2oc_exc_handler();
+ }
+ initialized = true;
+ }
+}
+EOS
+
+ # Compile init file.
+ init = File.join(objs_build_dir, 'init.mm')
+ init_o = File.join(objs_build_dir, 'init.o')
+ if !(File.exist?(init) and File.exist?(init_o) and File.read(init) == init_txt)
+ File.open(init, 'w') { |io| io.write(init_txt) }
+ sh "#{cxx} \"#{init}\" #{config.cflags(platform, true)} -c -o \"#{init_o}\""
+ end
+
+ if static_library
+ # Create a static archive with all object files + the runtime.
+ lib = File.join(config.versionized_build_dir(platform), config.name + '.a')
+ App.info 'Create', lib
+ cp File.join(datadir, platform, 'libmacruby-static.a'), lib
+ objs_list = objs.map { |path, _| path }.unshift(init_o, *config.frameworks_stubs_objects(platform)).map { |x| "\"#{x}\"" }.join(' ')
+ sh "/usr/bin/ar -cr \"#{lib}\" #{objs_list}"
+ return lib
+ end
+
+ # Generate main file.
+ main_txt = <<EOS
+#import <UIKit/UIKit.h>
+
+extern "C" {
+ void rb_define_const(void *, const char *, void *);
+ void rb_rb2oc_exc_handler(void);
+ void rb_exit(int);
+ void RubyMotionInit(int argc, char **argv);
+EOS
+ if config.spec_mode
+ spec_objs.each do |_, init_func|
+ main_txt << "void #{init_func}(void *, void *);\n"
+ end
end
main_txt << <<EOS
}
@@ -196,11 +262,31 @@ def build(config, platform)
@interface SpecLauncher : NSObject
@end
+#include <dlfcn.h>
+
@implementation SpecLauncher
+ (id)launcher
{
[UIApplication sharedApplication];
+
+ // Enable simulator accessibility.
+ // Thanks http://www.stewgleadow.com/blog/2011/10/14/enabling-accessibility-for-ios-applications/
+ NSString *simulatorRoot = [[[NSProcessInfo processInfo] environment] objectForKey:@"IPHONE_SIMULATOR_ROOT"];
+ if (simulatorRoot != nil) {
+ void *appSupportLibrary = dlopen([[simulatorRoot stringByAppendingPathComponent:@"/System/Library/PrivateFrameworks/AppSupport.framework/AppSupport"] fileSystemRepresentation], RTLD_LAZY);
+ CFStringRef (*copySharedResourcesPreferencesDomainForDomain)(CFStringRef domain) = (CFStringRef (*)(CFStringRef)) dlsym(appSupportLibrary, "CPCopySharedResourcesPreferencesDomainForDomain");
+
+ if (copySharedResourcesPreferencesDomainForDomain != NULL) {
+ CFStringRef accessibilityDomain = copySharedResourcesPreferencesDomainForDomain(CFSTR("com.apple.Accessibility"));
+
+ if (accessibilityDomain != NULL) {
+ CFPreferencesSetValue(CFSTR("ApplicationAccessibilityEnabled"), kCFBooleanTrue, accessibilityDomain, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
+ CFRelease(accessibilityDomain);
+ }
+ }
+ }
+
SpecLauncher *launcher = [[self alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:launcher selector:@selector(appLaunched:) name:UIApplicationDidBecomeActiveNotification object:nil];
return launcher;
@@ -230,18 +316,20 @@ def build(config, platform)
main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- const char *progname = argv[0];
- ruby_init();
- ruby_init_loadpath();
- ruby_script(progname);
int retval = 0;
try {
- void *self = rb_vm_top_self();
EOS
main_txt << "[SpecLauncher launcher];\n" if config.spec_mode
- app_objs.each do |_, init_func|
- main_txt << "#{init_func}(self, 0);\n"
- end
+ main_txt << <<EOS
+ RubyMotionInit(argc, argv);
+EOS
+ rubymotion_env =
+ if config.spec_mode
+ 'test'
+ else
+ config.development? ? 'development' : 'release'
+ end
+ main_txt << "rb_define_const([NSObject class], \"RUBYMOTION_ENV\", @\"#{rubymotion_env}\");\n"
main_txt << <<EOS
retval = UIApplicationMain(argc, argv, nil, @"#{config.delegate_class}");
rb_exit(retval);
@@ -279,14 +367,9 @@ def build(config, platform)
or vendor_libs.any? { |lib| File.mtime(lib) > File.mtime(main_exec) } \
or File.mtime(File.join(datadir, platform, 'libmacruby-static.a')) > File.mtime(main_exec)
App.info 'Link', main_exec
- objs_list = objs.map { |path, _| path }.unshift(main_o).map { |x| "\"#{x}\"" }.join(' ')
+ objs_list = objs.map { |path, _| path }.unshift(init_o, main_o, *config.frameworks_stubs_objects(platform)).map { |x| "\"#{x}\"" }.join(' ')
frameworks = config.frameworks_dependencies.map { |x| "-framework #{x}" }.join(' ')
- framework_stubs_objs = []
- config.frameworks_dependencies.each do |framework|
- stubs_obj = File.join(datadir, platform, "#{framework}_stubs.o")
- framework_stubs_objs << "\"#{stubs_obj}\"" if File.exist?(stubs_obj)
- end
- sh "#{cxx} -o \"#{main_exec}\" #{objs_list} #{framework_stubs_objs.join(' ')} #{config.ldflags(platform)} -L#{File.join(datadir, platform)} -lmacruby-static -lobjc -licucore #{frameworks} #{config.libs.join(' ')} #{vendor_libs.map { |x| '-force_load "' + x + '"' }.join(' ')}"
+ sh "#{cxx} -o \"#{main_exec}\" #{objs_list} #{config.ldflags(platform)} -L#{File.join(datadir, platform)} -lmacruby-static -lobjc -licucore #{frameworks} #{config.libs.join(' ')} #{vendor_libs.map { |x| '-force_load "' + x + '"' }.join(' ')}"
main_exec_created = true
end
@@ -289,10 +289,21 @@ def frameworks_dependencies
end
deps << framework
end
- deps.uniq.select { |dep| File.exist?(File.join(datadir, 'BridgeSupport', dep + '.bridgesupport')) }
+ deps = deps.uniq.select { |dep| File.exist?(File.join(datadir, 'BridgeSupport', dep + '.bridgesupport')) }
+ deps << 'UIAutomation' if spec_mode
+ deps
end
end
+ def frameworks_stubs_objects(platform)
+ stubs = []
+ frameworks_dependencies.each do |framework|
+ stubs_obj = File.join(datadir, platform, "#{framework}_stubs.o")
+ stubs << stubs_obj if File.exist?(stubs_obj)
+ end
+ stubs
+ end
+
def bridgesupport_files
@bridgesupport_files ||= begin
bs_files = []
@@ -311,7 +322,21 @@ def bridgesupport_files
end
def spec_files
- Dir.glob(File.join(specs_dir, '**', '*.rb'))
+ @spec_files ||= begin
+ # Core library + core helpers.
+ files = Dir.chdir(File.join(File.dirname(__FILE__), '..')) { (['spec.rb'] + Dir.glob('spec/helpers/*.rb')).map { |x| File.expand_path(x) } }
+ # Project helpers.
+ files += Dir.glob(File.join(specs_dir, 'helpers', '*.rb'))
+ # Project specs.
+ specs = Dir.glob(File.join(specs_dir, '*.rb'))
@alexrothenberg
alexrothenberg Jul 6, 2012

Is there a reason you removed the "**" as I have specs in subdirectories that are no longer found.

Could we change line 331 back to

specs = Dir.glob(File.join(specs_dir, '**', '*.rb'))

Thanks

@marcisme
marcisme Jul 7, 2012 Contributor

I ran into this too and submitted #19.

You can work around by doing something like this in your Rakefile.

Motion::Project::App.setup do |app|
  # ...
  app.specs_dir = 'spec/**'
end
+ if files_filter = ENV['files']
+ # Filter specs we want to run. A filter can be either the basename of a spec file or its path.
+ files_filter = files_filter.split(',')
+ files_filter.map! { |x| File.exist?(x) ? File.expand_path(x) : x }
+ specs.delete_if { |x| !files_filter.include?(File.expand_path(x)) and !files_filter.include?(File.basename(x, '.rb')) }
+ end
+ files + specs
+ end
end
def motiondir
@@ -389,7 +414,9 @@ def arch_flags(platform)
end
def common_flags(platform)
- "#{arch_flags(platform)} -isysroot \"#{sdk(platform)}\" -miphoneos-version-min=#{deployment_target} -F#{sdk(platform)}/System/Library/Frameworks"
+ cflags = "#{arch_flags(platform)} -isysroot \"#{sdk(platform)}\" -miphoneos-version-min=#{deployment_target} -F#{sdk(platform)}/System/Library/Frameworks"
+ cflags << " -F#{sdk(platform)}/Developer/Library/PrivateFrameworks" if spec_mode # For UIAutomation
+ cflags
end
def cflags(platform, cplusplus)
View
@@ -136,18 +136,14 @@ def handle_requirement_end(error)
def handle_summary; end
end
- extend case ENV['output']
- when nil, 'spec_dox'
- SpecDoxOutput # default
- when 'fast'
- FastOutput
- when 'test_unit'
- TestUnitOutput
- when 'tap'
- TapOutput
- when 'knock'
- KnockOutput
- end
+ Outputs = {
+ 'spec_dox' => SpecDoxOutput,
+ 'fast' => FastOutput,
+ 'test_unit' => TestUnitOutput,
+ 'tap' => TapOutput,
+ 'knock' => KnockOutput
+ }
+ extend(Outputs[ENV['output']] || SpecDoxOutput)
class Error < RuntimeError
attr_accessor :count_as
Oops, something went wrong.

0 comments on commit 5a37c3d

Please sign in to comment.