Permalink
Browse files

spec UI + static libraries support

  • Loading branch information...
lrz committed Jul 5, 2012
1 parent 8fac457 commit 5a37c3df38b7e3a759b0897e6c1774a7ed83f488
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'))

This comment has been minimized.

Show comment
Hide comment
@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

@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

This comment has been minimized.

Show comment
Hide comment
@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
@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.