Skip to content

Commit 5a37c3d

Browse files
committed
spec UI + static libraries support
1 parent 8fac457 commit 5a37c3d

File tree

6 files changed

+699
-39
lines changed

6 files changed

+699
-39
lines changed

lib/motion/project.rb

+11-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def create_ipa
134134
end
135135
end
136136

137-
desc "Run specs"
137+
desc "Run the test/spec suite"
138138
task :spec do
139139
App.config.spec_mode = true
140140
Rake::Task["simulator"].invoke
@@ -182,6 +182,16 @@ def create_ipa
182182
end
183183
end
184184

185+
desc "Create a .a static library"
186+
task :static do
187+
libs = %w{iPhoneSimulator iPhoneOS}.map do |platform|
188+
'"' + App.build(platform, :static => true) + '"'
189+
end
190+
fat_lib = File.join(App.config.build_dir, App.config.name + '-universal.a')
191+
App.info 'Create', fat_lib
192+
sh "/usr/bin/lipo -create #{libs.join(' ')} -output \"#{fat_lib}\""
193+
end
194+
185195
=begin
186196
# Automatically load project extensions. A project extension is a gem whose
187197
# name starts with `motion-' and which exposes a `lib/motion/project' libdir.

lib/motion/project/app.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ def setup
6464
config.validate
6565
end
6666

67-
def build(platform)
68-
builder.build(config, platform)
67+
def build(platform, opts={})
68+
builder.build(config, platform, opts)
6969
end
7070

7171
def archive

lib/motion/project/builder.rb

+104-21
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ module Motion; module Project;
2727
class Builder
2828
include Rake::DSL if Rake.const_defined?(:DSL)
2929

30-
def build(config, platform)
30+
def build(config, platform, opts)
3131
datadir = config.datadir
3232
archs = config.archs[platform]
3333

34+
static_library = opts.delete(:static)
35+
3436
ruby = File.join(config.bindir, 'ruby')
3537
llc = File.join(config.bindir, 'llc')
3638

@@ -160,15 +162,15 @@ def build(config, platform)
160162
FileUtils.touch(objs_build_dir) if project_file_changed
161163

162164
app_objs = objs
165+
spec_objs = []
163166
if config.spec_mode
164167
# Build spec files too, but sequentially.
165-
objs << build_file.call(File.expand_path(File.join(File.dirname(__FILE__), '../spec.rb')))
166168
spec_objs = config.spec_files.map { |path| build_file.call(path) }
167169
objs += spec_objs
168170
end
169171

170-
# Generate main file.
171-
main_txt = <<EOS
172+
# Generate init file.
173+
init_txt = <<EOS
172174
#import <UIKit/UIKit.h>
173175
174176
extern "C" {
@@ -184,8 +186,72 @@ def build(config, platform)
184186
void rb_rb2oc_exc_handler(void);
185187
void rb_exit(int);
186188
EOS
187-
objs.each do |_, init_func|
188-
main_txt << "void #{init_func}(void *, void *);\n"
189+
app_objs.each do |_, init_func|
190+
init_txt << "void #{init_func}(void *, void *);\n"
191+
end
192+
init_txt << <<EOS
193+
}
194+
195+
extern "C"
196+
void
197+
RubyMotionInit(int argc, char **argv)
198+
{
199+
static bool initialized = false;
200+
if (!initialized) {
201+
ruby_init();
202+
ruby_init_loadpath();
203+
if (argc > 0) {
204+
const char *progname = argv[0];
205+
ruby_script(progname);
206+
}
207+
try {
208+
void *self = rb_vm_top_self();
209+
EOS
210+
app_objs.each do |_, init_func|
211+
init_txt << "#{init_func}(self, 0);\n"
212+
end
213+
init_txt << <<EOS
214+
}
215+
catch (...) {
216+
rb_rb2oc_exc_handler();
217+
}
218+
initialized = true;
219+
}
220+
}
221+
EOS
222+
223+
# Compile init file.
224+
init = File.join(objs_build_dir, 'init.mm')
225+
init_o = File.join(objs_build_dir, 'init.o')
226+
if !(File.exist?(init) and File.exist?(init_o) and File.read(init) == init_txt)
227+
File.open(init, 'w') { |io| io.write(init_txt) }
228+
sh "#{cxx} \"#{init}\" #{config.cflags(platform, true)} -c -o \"#{init_o}\""
229+
end
230+
231+
if static_library
232+
# Create a static archive with all object files + the runtime.
233+
lib = File.join(config.versionized_build_dir(platform), config.name + '.a')
234+
App.info 'Create', lib
235+
cp File.join(datadir, platform, 'libmacruby-static.a'), lib
236+
objs_list = objs.map { |path, _| path }.unshift(init_o, *config.frameworks_stubs_objects(platform)).map { |x| "\"#{x}\"" }.join(' ')
237+
sh "/usr/bin/ar -cr \"#{lib}\" #{objs_list}"
238+
return lib
239+
end
240+
241+
# Generate main file.
242+
main_txt = <<EOS
243+
#import <UIKit/UIKit.h>
244+
245+
extern "C" {
246+
void rb_define_const(void *, const char *, void *);
247+
void rb_rb2oc_exc_handler(void);
248+
void rb_exit(int);
249+
void RubyMotionInit(int argc, char **argv);
250+
EOS
251+
if config.spec_mode
252+
spec_objs.each do |_, init_func|
253+
main_txt << "void #{init_func}(void *, void *);\n"
254+
end
189255
end
190256
main_txt << <<EOS
191257
}
@@ -196,11 +262,31 @@ def build(config, platform)
196262
@interface SpecLauncher : NSObject
197263
@end
198264
265+
#include <dlfcn.h>
266+
199267
@implementation SpecLauncher
200268
201269
+ (id)launcher
202270
{
203271
[UIApplication sharedApplication];
272+
273+
// Enable simulator accessibility.
274+
// Thanks http://www.stewgleadow.com/blog/2011/10/14/enabling-accessibility-for-ios-applications/
275+
NSString *simulatorRoot = [[[NSProcessInfo processInfo] environment] objectForKey:@"IPHONE_SIMULATOR_ROOT"];
276+
if (simulatorRoot != nil) {
277+
void *appSupportLibrary = dlopen([[simulatorRoot stringByAppendingPathComponent:@"/System/Library/PrivateFrameworks/AppSupport.framework/AppSupport"] fileSystemRepresentation], RTLD_LAZY);
278+
CFStringRef (*copySharedResourcesPreferencesDomainForDomain)(CFStringRef domain) = (CFStringRef (*)(CFStringRef)) dlsym(appSupportLibrary, "CPCopySharedResourcesPreferencesDomainForDomain");
279+
280+
if (copySharedResourcesPreferencesDomainForDomain != NULL) {
281+
CFStringRef accessibilityDomain = copySharedResourcesPreferencesDomainForDomain(CFSTR("com.apple.Accessibility"));
282+
283+
if (accessibilityDomain != NULL) {
284+
CFPreferencesSetValue(CFSTR("ApplicationAccessibilityEnabled"), kCFBooleanTrue, accessibilityDomain, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
285+
CFRelease(accessibilityDomain);
286+
}
287+
}
288+
}
289+
204290
SpecLauncher *launcher = [[self alloc] init];
205291
[[NSNotificationCenter defaultCenter] addObserver:launcher selector:@selector(appLaunched:) name:UIApplicationDidBecomeActiveNotification object:nil];
206292
return launcher;
@@ -230,18 +316,20 @@ def build(config, platform)
230316
main(int argc, char **argv)
231317
{
232318
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
233-
const char *progname = argv[0];
234-
ruby_init();
235-
ruby_init_loadpath();
236-
ruby_script(progname);
237319
int retval = 0;
238320
try {
239-
void *self = rb_vm_top_self();
240321
EOS
241322
main_txt << "[SpecLauncher launcher];\n" if config.spec_mode
242-
app_objs.each do |_, init_func|
243-
main_txt << "#{init_func}(self, 0);\n"
244-
end
323+
main_txt << <<EOS
324+
RubyMotionInit(argc, argv);
325+
EOS
326+
rubymotion_env =
327+
if config.spec_mode
328+
'test'
329+
else
330+
config.development? ? 'development' : 'release'
331+
end
332+
main_txt << "rb_define_const([NSObject class], \"RUBYMOTION_ENV\", @\"#{rubymotion_env}\");\n"
245333
main_txt << <<EOS
246334
retval = UIApplicationMain(argc, argv, nil, @"#{config.delegate_class}");
247335
rb_exit(retval);
@@ -279,14 +367,9 @@ def build(config, platform)
279367
or vendor_libs.any? { |lib| File.mtime(lib) > File.mtime(main_exec) } \
280368
or File.mtime(File.join(datadir, platform, 'libmacruby-static.a')) > File.mtime(main_exec)
281369
App.info 'Link', main_exec
282-
objs_list = objs.map { |path, _| path }.unshift(main_o).map { |x| "\"#{x}\"" }.join(' ')
370+
objs_list = objs.map { |path, _| path }.unshift(init_o, main_o, *config.frameworks_stubs_objects(platform)).map { |x| "\"#{x}\"" }.join(' ')
283371
frameworks = config.frameworks_dependencies.map { |x| "-framework #{x}" }.join(' ')
284-
framework_stubs_objs = []
285-
config.frameworks_dependencies.each do |framework|
286-
stubs_obj = File.join(datadir, platform, "#{framework}_stubs.o")
287-
framework_stubs_objs << "\"#{stubs_obj}\"" if File.exist?(stubs_obj)
288-
end
289-
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(' ')}"
372+
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(' ')}"
290373
main_exec_created = true
291374
end
292375

lib/motion/project/config.rb

+30-3
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,21 @@ def frameworks_dependencies
289289
end
290290
deps << framework
291291
end
292-
deps.uniq.select { |dep| File.exist?(File.join(datadir, 'BridgeSupport', dep + '.bridgesupport')) }
292+
deps = deps.uniq.select { |dep| File.exist?(File.join(datadir, 'BridgeSupport', dep + '.bridgesupport')) }
293+
deps << 'UIAutomation' if spec_mode
294+
deps
293295
end
294296
end
295297

298+
def frameworks_stubs_objects(platform)
299+
stubs = []
300+
frameworks_dependencies.each do |framework|
301+
stubs_obj = File.join(datadir, platform, "#{framework}_stubs.o")
302+
stubs << stubs_obj if File.exist?(stubs_obj)
303+
end
304+
stubs
305+
end
306+
296307
def bridgesupport_files
297308
@bridgesupport_files ||= begin
298309
bs_files = []
@@ -311,7 +322,21 @@ def bridgesupport_files
311322
end
312323

313324
def spec_files
314-
Dir.glob(File.join(specs_dir, '**', '*.rb'))
325+
@spec_files ||= begin
326+
# Core library + core helpers.
327+
files = Dir.chdir(File.join(File.dirname(__FILE__), '..')) { (['spec.rb'] + Dir.glob('spec/helpers/*.rb')).map { |x| File.expand_path(x) } }
328+
# Project helpers.
329+
files += Dir.glob(File.join(specs_dir, 'helpers', '*.rb'))
330+
# Project specs.
331+
specs = Dir.glob(File.join(specs_dir, '*.rb'))
332+
if files_filter = ENV['files']
333+
# Filter specs we want to run. A filter can be either the basename of a spec file or its path.
334+
files_filter = files_filter.split(',')
335+
files_filter.map! { |x| File.exist?(x) ? File.expand_path(x) : x }
336+
specs.delete_if { |x| !files_filter.include?(File.expand_path(x)) and !files_filter.include?(File.basename(x, '.rb')) }
337+
end
338+
files + specs
339+
end
315340
end
316341

317342
def motiondir
@@ -389,7 +414,9 @@ def arch_flags(platform)
389414
end
390415

391416
def common_flags(platform)
392-
"#{arch_flags(platform)} -isysroot \"#{sdk(platform)}\" -miphoneos-version-min=#{deployment_target} -F#{sdk(platform)}/System/Library/Frameworks"
417+
cflags = "#{arch_flags(platform)} -isysroot \"#{sdk(platform)}\" -miphoneos-version-min=#{deployment_target} -F#{sdk(platform)}/System/Library/Frameworks"
418+
cflags << " -F#{sdk(platform)}/Developer/Library/PrivateFrameworks" if spec_mode # For UIAutomation
419+
cflags
393420
end
394421

395422
def cflags(platform, cplusplus)

lib/motion/spec.rb

+8-12
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,14 @@ def handle_requirement_end(error)
136136
def handle_summary; end
137137
end
138138

139-
extend case ENV['output']
140-
when nil, 'spec_dox'
141-
SpecDoxOutput # default
142-
when 'fast'
143-
FastOutput
144-
when 'test_unit'
145-
TestUnitOutput
146-
when 'tap'
147-
TapOutput
148-
when 'knock'
149-
KnockOutput
150-
end
139+
Outputs = {
140+
'spec_dox' => SpecDoxOutput,
141+
'fast' => FastOutput,
142+
'test_unit' => TestUnitOutput,
143+
'tap' => TapOutput,
144+
'knock' => KnockOutput
145+
}
146+
extend(Outputs[ENV['output']] || SpecDoxOutput)
151147

152148
class Error < RuntimeError
153149
attr_accessor :count_as

0 commit comments

Comments
 (0)