def build_modules_info(self, resources_dir, app_bin_dir): self.app_modules = [] (modules, external_child_modules) = bindings.get_all_module_bindings() compiler = Compiler(self.tiapp, resources_dir, self.java, app_bin_dir, os.path.dirname(app_bin_dir)) compiler.compile(compile_bytecode=False, info_message=None) for module in compiler.modules: module_bindings = [] # TODO: we should also detect module properties for method in compiler.module_methods: if method.lower().startswith(module + ".") and "." not in method: module_bindings.append(method[len(module) + 1 :]) module_class = None module_apiName = None for m in modules.keys(): if modules[m]["fullAPIName"].lower() == module: module_class = m module_apiName = modules[m]["fullAPIName"] break if module_apiName == None: continue # module wasn't found if "." not in module: ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[module_class]: if child_module["fullAPIName"].lower() in compiler.modules: ext_modules.append(child_module) self.app_modules.append( { "api_name": module_apiName, "class_name": module_class, "bindings": module_bindings, "external_child_modules": ext_modules, } ) # discover app modules detector = ModuleDetector(self.project_dir) missing, detected_modules = detector.find_app_modules(self.tiapp, "android") for missing_module in missing: print "[WARN] Couldn't find app module: %s" % missing_module["id"] self.custom_modules = [] for module in detected_modules: if module.jar == None: continue module_jar = zipfile.ZipFile(module.jar) module_bindings = bindings.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings["modules"].keys(): module_id = module_bindings["proxies"][module_class]["proxyAttrs"]["id"] print "[DEBUG] module_id = %s" % module_id if module_id == module.manifest.moduleid: print "[DEBUG] appending module: %s" % module_class self.custom_modules.append({"class_name": module_class, "manifest": module.manifest})
def build_modules_info(self, resources_dir, app_bin_dir, include_all_ti_modules=False): self.app_modules = [] (modules, external_child_modules) = bindings.get_all_module_bindings() compiler = Compiler(self.tiapp, resources_dir, self.java, app_bin_dir, os.path.dirname(app_bin_dir), include_all_modules=include_all_ti_modules) compiler.compile(compile_bytecode=False, info_message=None) for module in compiler.modules: module_bindings = [] # TODO: we should also detect module properties for method in compiler.module_methods: if method.lower().startswith(module+'.') and '.' not in method: module_bindings.append(method[len(module)+1:]) module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower() == module: module_class = m module_apiName = modules[m]['fullAPIName'] break if module_apiName == None: continue # module wasn't found if '.' not in module: ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[module_class]: if child_module['fullAPIName'].lower() in compiler.modules: ext_modules.append(child_module) self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': module_bindings, 'external_child_modules': ext_modules }) # discover app modules detector = ModuleDetector(self.project_dir) missing, detected_modules = detector.find_app_modules(self.tiapp, 'android') for missing_module in missing: print '[WARN] Couldn\'t find app module: %s' % missing_module['id'] self.custom_modules = [] for module in detected_modules: if module.jar == None: continue module_jar = zipfile.ZipFile(module.jar) module_bindings = bindings.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings['modules'].keys(): module_id = module_bindings['proxies'][module_class]['proxyAttrs']['id'] print '[DEBUG] module_id = %s' % module_id if module_id == module.manifest.moduleid: print '[DEBUG] appending module: %s' % module_class self.custom_modules.append({ 'class_name': module_class, 'manifest': module.manifest })
def build_modules_info(self, resources_dir, app_bin_dir): self.app_modules = [] (modules, external_child_modules) = bindings.get_all_module_bindings() compiler = Compiler(self.tiapp, resources_dir, self.java, app_bin_dir, os.path.dirname(app_bin_dir)) compiler.compile(compile_bytecode=False, info_message=None) for module in compiler.modules: module_bindings = [] # TODO: we should also detect module properties for method in compiler.module_methods: if method.lower().startswith(module+'.') and '.' not in method: module_bindings.append(method[len(module)+1:]) module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower() == module: module_class = m module_apiName = modules[m]['fullAPIName'] break if module_apiName == None: continue # module wasn't found if '.' not in module: ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[module_class]: if child_module['fullAPIName'].lower() in compiler.modules: ext_modules.append(child_module) self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': module_bindings, 'external_child_modules': ext_modules }) # discover app modules detector = ModuleDetector(self.project_dir) missing, detected_modules = detector.find_app_modules(self.tiapp, 'android') for missing_module in missing: print '[WARN] Couldn\'t find app module: %s' % missing_module['id'] self.custom_modules = [] for module in detected_modules: if module.jar == None: continue module_jar = zipfile.ZipFile(module.jar) module_bindings = bindings.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings['modules'].keys(): module_id = module_bindings['proxies'][module_class]['proxyAttrs']['id'] print '[DEBUG] module_id = %s' % module_id if module_id == module.manifest.moduleid: print '[DEBUG] appending module: %s' % module_class self.custom_modules.append({ 'class_name': module_class, 'manifest': module.manifest })
def __init__(self,project_dir,appid,name,deploytype,xcode,devicefamily,iphone_version,silent=False): self.project_dir = project_dir self.project_name = name self.appid = appid self.iphone_dir = os.path.join(project_dir,'build','iphone') self.classes_dir = os.path.join(self.iphone_dir,'Classes') self.modules = [] self.modules_metadata = [] # for now, these are required self.defines = ['USE_TI_ANALYTICS','USE_TI_NETWORK','USE_TI_PLATFORM','USE_TI_UI'] tiapp_xml = os.path.join(project_dir,'tiapp.xml') ti = TiAppXML(tiapp_xml) sdk_version = os.path.basename(os.path.abspath(os.path.join(template_dir,'../'))) detector = ModuleDetector(project_dir) missing_modules, modules = detector.find_app_modules(ti, 'iphone') if xcode: app_name = os.environ['FULL_PRODUCT_NAME'] app_dir = os.path.join(os.environ['TARGET_BUILD_DIR'],os.environ['CONTENTS_FOLDER_PATH']) else: target = 'Debug' if deploytype == 'install': target = 'Release' app_name = name+'.app' app_folder_name = '%s-iphoneos' % target app_dir = os.path.abspath(os.path.join(self.iphone_dir,'build',app_folder_name,app_name)) main_template_file = os.path.join(template_dir,'main.m') main_template = codecs.open(main_template_file, encoding='utf-8').read() main_template = main_template.replace('__PROJECT_NAME__',name) main_template = main_template.replace('__PROJECT_ID__',appid) main_template = main_template.replace('__DEPLOYTYPE__',deploytype) main_template = main_template.replace('__APP_ID__',appid) main_template = main_template.replace('__APP_ANALYTICS__',ti.properties['analytics']) main_template = main_template.replace('__APP_PUBLISHER__',ti.properties['publisher']) main_template = main_template.replace('__APP_URL__',ti.properties['url']) main_template = main_template.replace('__APP_NAME__',ti.properties['name']) main_template = main_template.replace('__APP_VERSION__',ti.properties['version']) main_template = main_template.replace('__APP_DESCRIPTION__',ti.properties['description']) main_template = main_template.replace('__APP_COPYRIGHT__',ti.properties['copyright']) main_template = main_template.replace('__APP_GUID__',ti.properties['guid']) if deploytype=='development': main_template = main_template.replace('__APP_RESOURCE_DIR__',os.path.abspath(os.path.join(project_dir,'Resources'))) else: main_template = main_template.replace('__APP_RESOURCE_DIR__','') if not silent: print "[INFO] Titanium SDK version: %s" % sdk_version print "[INFO] iPhone Device family: %s" % devicefamily print "[INFO] iPhone SDK version: %s" % iphone_version main_template_out = os.path.join(self.iphone_dir,'main.m') main_file = codecs.open(main_template_out,'w+',encoding='utf-8') main_file_contents = main_file.read() if main_file_contents!=main_template: main_file.write(main_template) main_file.close() if deploytype == 'production': version = ti.properties['version'] # we want to make sure in debug mode the version always changes version = "%s.%d" % (version,time.time()) ti.properties['version']=version resources_dir = os.path.join(project_dir,'Resources') iphone_resources_dir = os.path.join(resources_dir,'iphone') # copy in any resources in our module like icons project_module_dir = os.path.join(project_dir,'modules','iphone') if os.path.exists(project_module_dir): self.copy_resources([project_module_dir],app_dir,False) # we have to copy these even in simulator given the path difference if os.path.exists(app_dir): self.copy_resources([iphone_resources_dir],app_dir,False) # generate the includes for all compiled modules xcconfig_c = "// this is a generated file - DO NOT EDIT\n\n" has_modules = False if len(modules) > 0: mods = open(os.path.join(self.classes_dir,'ApplicationMods.m'),'w+') mods.write(MODULE_IMPL_HEADER) for module in modules: module_id = module.manifest.moduleid.lower() module_name = module.manifest.name.lower() module_version = module.manifest.version module_guid = '' module_licensekey = '' if module.manifest.has_property('guid'): module_guid = module.manifest.guid if module.manifest.has_property('licensekey'): module_licensekey = module.manifest.licensekey self.modules_metadata.append({'guid':module_guid,'name':module_name,'id':module_id,'dir':module.path,'version':module_version,'licensekey':module_licensekey}) xcfile = module.get_resource('module.xcconfig') if os.path.exists(xcfile): xcconfig_c+="#include \"%s\"\n" % xcfile xcfile = os.path.join(self.project_dir,'modules','iphone',"%s.xcconfig" % module_name) if os.path.exists(xcfile): xcconfig_c+="#include \"%s\"\n" % xcfile mods.write(" [modules addObject:[NSDictionary dictionaryWithObjectsAndKeys:@\"%s\",@\"name\",@\"%s\",@\"moduleid\",@\"%s\",@\"version\",@\"%s\",@\"guid\",@\"%s\",@\"licensekey\",nil]];\n" % (module_name,module_id,module_version,module_guid,module_licensekey)); mods.write(" return modules;\n") mods.write("}\n") mods.write(FOOTER) mods.close() has_modules = True xcconfig = os.path.join(self.iphone_dir,"module.xcconfig") make_xcc = True if os.path.exists(xcconfig): existing_xcc = open(xcconfig).read() # only copy if different so we don't trigger re-compile in xcode make_xcc = existing_xcc!=xcconfig_c if make_xcc: xcconfig = open(xcconfig,'w') xcconfig.write(xcconfig_c) xcconfig.close() if deploytype=='simulator': shutil.copy(os.path.join(template_dir,'Classes','defines.h'),os.path.join(self.classes_dir,'defines.h')) if deploytype!='development' or has_modules: if os.path.exists(app_dir): self.copy_resources([resources_dir],app_dir) if deploytype!='development': defines_file = os.path.join(self.classes_dir,'defines.h') defines_header = open(defines_file,'w+') defines_content = "// Warning: this is generated file. Do not modify!\n\n" defines_content+= "#define TI_VERSION %s\n"%sdk_version for sym in self.defines: defines_content+="#define %s 1\n"%sym if defines_content!=defines_header.read(): defines_header.write(defines_content) defines_header.close() # deploy any module image files for module in self.modules: img_dir = os.path.join(template_dir,'modules',module.lower(),'images') print "[DEBUG] module image = %s" % img_dir if not os.path.exists(img_dir): continue dest_img_dir = os.path.join(app_dir,'modules',module.lower(),'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) self.copy_resources([img_dir],dest_img_dir,False) if deploytype!='development' and os.path.exists(app_dir): # optimize PNGs - since we don't include them in the Resources of the xcodeproj # the ones we copy in won't get optimized so we need to run it manually # we can skip this on the simulator but should do it on device dev_path = "/Developer" # we need to ask xcode where the root path is path = run.run(["/usr/bin/xcode-select","-print-path"],True,False) if path: dev_path = path.strip() run.run(["%s/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize"%dev_path,app_dir],False) # remove empty directories os.chdir(app_dir) os.system("find . -type d -empty -delete") else: print "[INFO] Skipping JS compile, running from simulator"
def main(args): if len(args) < 2: print "Usage: %s <project_directory> [sdk_verison]" % os.path.basename(args[0]) sys.exit(1) # What needs to be done in order to perform a "true" export? # --- # Wipe the build dir # Migrate resources # Migrate tiapp.xml (required for scripts) # Generate project from template # Populate Info.plist # Compile/migrate i18n # Migrate scripts for compiling JSS files (and i18n) # Modify xcodeproj build steps to call the JSS compiler # Then... Share and Enjoy. project_dir = os.path.abspath(args[1]) build_dir = os.path.join(project_dir,'build','iphone') titanium_local = os.path.join(build_dir,'titanium') if len(args) == 3: version = args[2] sdk_dir = find_sdk(version) else: sdk_dir = os.path.abspath(os.path.dirname(template_dir)) version = os.path.basename(sdk_dir) tiappxml = os.path.join(project_dir, 'tiapp.xml') tiapp = TiAppXML(tiappxml) app_id = tiapp.properties['id'] app_name = tiapp.properties['name'] if app_id is None or app_name is None: info("Your tiapp.xml is malformed - please specify an app name and id") sys.exit(1) # Clean build dir (if it exists), error otherwise (no iphone support) info("Cleaning build...") if os.path.exists(build_dir): for f in os.listdir(build_dir): path = os.path.join(build_dir,f) if os.path.isfile(path): os.remove(path) else: shutil.rmtree(path) else: info("Your project is not configured to be built for iphone.") exit(1) # Migrate Resources info("Migrating resources...") project_resources = os.path.join(project_dir, 'Resources') resources_dir = os.path.join(build_dir, 'Resources') shutil.copytree(project_resources,resources_dir) # Migrate platform/iphone contents into Resources. info("Migrating platform/iphone to Resources...") project_platform = os.path.join(project_dir,'platform','iphone') if os.path.isdir(project_platform): contents = os.listdir(project_platform) for file in contents: path = os.path.join(project_platform,file) if os.path.isdir(path): shutil.copytree(path, os.path.join(resources_dir,file)) else: shutil.copy(path, os.path.join(resources_dir,file)) # Migrate tiapp.xml info("Migrating tiapp.xml...") shutil.copy(tiappxml, build_dir) # Generate project stuff from the template info("Generating project from Titanium template...") project = Projector(app_name,version,template_dir,project_dir,app_id) project.create(template_dir,build_dir) # Because the debugger.plist is built as part of the required # resources, we need to autogen an empty one debug_plist = os.path.join(resources_dir,'debugger.plist') force_xcode = write_debugger_plist(None, None, template_dir, debug_plist) # Populate Info.plist applogo = None info("Populating Info.plist...") plist_out = os.path.join(build_dir, 'Info.plist') create_info_plist(tiapp, template_dir, project_dir, plist_out) applogo = tiapp.generate_infoplist(plist_out, app_id, 'iphone', project_dir, None) # Run the compiler to autogenerate .m files info("Copying classes, creating generated .m files...") compiler = Compiler(project_dir,app_id,app_name,'export') compiler.compileProject(silent=True) #... But we still have to nuke the stuff that gets built that we don't want # to bundle. ios_build = os.path.join(build_dir,'build') if os.path.isdir(ios_build): shutil.rmtree(os.path.join(build_dir,'build')) # Install applogo/splash/etc. info("Copying icons and splash...") install_logo(tiapp, applogo, project_dir, template_dir, resources_dir) install_defaults(project_dir, template_dir, resources_dir) # Get Modules detector = ModuleDetector(project_dir) missing_modules, modules = detector.find_app_modules(tiapp, 'iphone') if len(missing_modules) != 0: for module in missing_modules: info("MISSING MODULE: %s ... Project will not build correctly" % module['id']) info("Terminating export: Please fix your modules.") sys.exit(1) module_search_path, module_asset_dirs = locate_modules(modules, project_dir, resources_dir, info) lib_dir = os.path.join(build_dir, 'lib') if not os.path.exists(lib_dir): os.makedirs(lib_dir) if len(module_search_path) > 0: info("Copying modules...") for module in module_search_path: module_name, module_path = module info("\t%s..." % module_name) shutil.copy(os.path.join(module_path, module_name), lib_dir) module[1] = os.path.join(lib_dir, module_name) info("Copying module metadata...") metadata_dir = os.path.join(build_dir, 'metadata') for module in modules: module_metadata = os.path.join(module.path,'metadata.json') if os.path.exists(module_metadata): if not os.path.exists(metadata_dir): os.makedirs(metadata_dir) target = os.path.join(metadata_dir, "%s.json" % module.manifest.moduleid) shutil.copyfile(module_metadata, target) # Note: The module link information has to be added to # the xcodeproj after it's created. # We also have to mod the module_search_path to reference # the local 'lib' directory instead of the original # module install location info("Linking modules...") local_modules = [] for module in module_search_path: name = module[0] newpath = os.path.join('lib',name) local_modules.append([name, newpath]) link_modules(local_modules, app_name, build_dir, relative=True) # Copy libraries info("Copying libraries...") iphone_dir = os.path.join(sdk_dir, 'iphone') for lib in glob.iglob(os.path.join(iphone_dir,'lib*')): info("\t%s..." % lib) shutil.copy(lib, lib_dir) # Process i18n files info("Processing i18n...") locale_compiler = LocaleCompiler(app_name, project_dir, 'ios', 'development', resources_dir) locale_compiler.compile() # Migrate compile scripts info("Copying custom Titanium compiler scripts...") shutil.copytree(os.path.join(sdk_dir,'common'),titanium_local) shutil.copy(os.path.join(sdk_dir,'tiapp.py'),titanium_local) iphone_script_dir = os.path.join(titanium_local,'iphone') os.mkdir(iphone_script_dir) shutil.copy(os.path.join(sdk_dir,'iphone','compiler.py'),iphone_script_dir) shutil.copy(os.path.join(sdk_dir,'iphone','run.py'),iphone_script_dir) shutil.copy(os.path.join(sdk_dir,'iphone','csspacker.py'),iphone_script_dir) shutil.copy(os.path.join(sdk_dir,'iphone','jspacker.py'),iphone_script_dir) shutil.copy(os.path.join(sdk_dir,'iphone','titanium_prep'),iphone_script_dir) # Add compilation to the build script in project info("Modifying pre-compile stage...") xcodeproj = os.path.join(build_dir,'%s.xcodeproj' % app_name, 'project.pbxproj') contents = codecs.open(xcodeproj,'r',encoding='utf-8').read() css_compiler = os.path.join('titanium','css','csscompiler.py') ti_compiler = os.path.join('titanium','iphone','compiler.py') script = """%s . ios Resources %s . export-build $TARGETED_DEVICE_FAMILY $SDKROOT %s""" % (css_compiler, ti_compiler, version) contents = fix_xcode_script(contents,"Pre-Compile",script) # write our new project f = codecs.open(xcodeproj,'w',encoding='utf-8') f.write(contents) f.close() info("Finished! Share and Enjoy.")
def compileProject(self, xcode=False, devicefamily='ios', iphone_version='iphoneos', silent=False, sdk=None): tiapp_xml = os.path.join(self.project_dir, 'tiapp.xml') ti = TiAppXML(tiapp_xml) if sdk is None: sdk_version = os.path.basename( os.path.abspath(os.path.join(template_dir, '../'))) else: sdk_version = sdk if xcode: app_name = os.environ['FULL_PRODUCT_NAME'] app_dir = os.path.join(os.environ['TARGET_BUILD_DIR'], os.environ['CONTENTS_FOLDER_PATH']) else: target = 'Debug' if self.deploytype == 'production': target = 'Release' app_name = self.project_name + '.app' app_folder_name = '%s-iphoneos' % target app_dir = os.path.abspath( os.path.join(self.iphone_dir, 'build', app_folder_name, app_name)) if not silent: print "[INFO] Titanium SDK version: %s" % sdk_version print "[INFO] iPhone Device family: %s" % devicefamily print "[INFO] iPhone SDK version: %s" % iphone_version if self.deploytype != 'export-build': main_template_file = os.path.join(template_dir, 'main.m') main_template = codecs.open(main_template_file, encoding='utf-8').read() main_template = main_template.replace('__PROJECT_NAME__', self.project_name) main_template = main_template.replace('__PROJECT_ID__', self.appid) main_template = main_template.replace('__DEPLOYTYPE__', self.deploytype) main_template = main_template.replace('__APP_ID__', self.appid) main_template = main_template.replace('__APP_ANALYTICS__', ti.properties['analytics']) main_template = main_template.replace('__APP_PUBLISHER__', ti.properties['publisher']) main_template = main_template.replace('__APP_URL__', ti.properties['url']) main_template = main_template.replace('__APP_NAME__', ti.properties['name']) main_template = main_template.replace('__APP_VERSION__', ti.properties['version']) main_template = main_template.replace('__APP_DESCRIPTION__', ti.properties['description']) main_template = main_template.replace('__APP_COPYRIGHT__', ti.properties['copyright']) main_template = main_template.replace('__APP_GUID__', ti.properties['guid']) main_template = main_template.replace('__APP_RESOURCE_DIR__', '') main_template_out = os.path.join(self.iphone_dir, 'main.m') main_file = codecs.open(main_template_out, 'w+', encoding='utf-8') main_file_contents = main_file.read() if main_file_contents != main_template: main_file.write(main_template) main_file.close() resources_dir = os.path.join(self.project_dir, 'Resources') iphone_resources_dir = os.path.join(resources_dir, 'iphone') iphone_platform_dir = os.path.join(self.project_dir, 'platform', 'iphone') # copy in any resources in our module like icons # NOTE: This means that any JS-only modules in the local project # are hashed up and dumped into the export. has_modules = False missing_modules, modules, module_js = ([], [], []) module_js_dir = os.path.join(self.project_dir, 'modules') if os.path.exists(module_js_dir): for file in os.listdir(module_js_dir): if file.endswith('.js'): module_js.append({ 'from': os.path.join(module_js_dir, file), 'to': os.path.join(app_dir, file), 'path': 'modules/' + file }) if self.deploytype != 'export-build': # Have to load the module detection here, in order to # prevent distributing even MORE stuff in export/transport sys.path.append(os.path.join(template_dir, '../module')) from module import ModuleDetector detector = ModuleDetector(self.project_dir) missing_modules, modules = detector.find_app_modules(ti, 'iphone') # we have to copy these even in simulator given the path difference if os.path.exists(app_dir): self.copy_resources([iphone_resources_dir], app_dir, False) if os.path.exists(app_dir): self.copy_resources([iphone_platform_dir], app_dir, False) # generate the includes for all compiled modules xcconfig_c = "// this is a generated file - DO NOT EDIT\n\n" if len(modules) > 0: mods = open( os.path.join(self.classes_dir, 'ApplicationMods.m'), 'w+') variables = {} mods.write(MODULE_IMPL_HEADER) for module in modules: if module.js: # CommonJS module module_js.append({ 'from': module.js, 'path': 'modules/' + os.path.basename(module.js) }) module_id = module.manifest.moduleid.lower() module_name = module.manifest.name.lower() module_version = module.manifest.version module_guid = '' module_licensekey = '' if module.manifest.has_property('guid'): module_guid = module.manifest.guid if module.manifest.has_property('licensekey'): module_licensekey = module.manifest.licensekey self.modules_metadata.append({ 'guid': module_guid, 'name': module_name, 'id': module_id, 'dir': module.path, 'version': module_version, 'licensekey': module_licensekey }) xcfile = module.get_resource('module.xcconfig') if os.path.exists(xcfile): xcconfig_contents = parse_xcconfig( xcfile, module_id, variables) xcconfig_c += xcconfig_contents xcfile = os.path.join(self.project_dir, 'modules', 'iphone', "%s.xcconfig" % module_name) if os.path.exists(xcfile): xcconfig_contents = parse_xcconfig( xcfile, module_id, variables) xcconfig_c += xcconfig_contents mods.write( " [modules addObject:[NSDictionary dictionaryWithObjectsAndKeys:@\"%s\",@\"name\",@\"%s\",@\"moduleid\",@\"%s\",@\"version\",@\"%s\",@\"guid\",@\"%s\",@\"licensekey\",nil]];\n" % (module_name, module_id, module_version, module_guid, module_licensekey)) # Load export symbols from modules... metadata_path = os.path.join(module.path, 'metadata.json') if os.path.exists(metadata_path): self.load_metadata(metadata_path) mods.write(" return modules;\n") mods.write("}\n") mods.write(FOOTER) mods.close() for (name, values) in variables.iteritems(): xcconfig_c += name + '=$(inherited) ' for value in values: xcconfig_c += '$(%s) ' % value xcconfig_c += '\n' has_modules = True xcconfig = os.path.join(self.iphone_dir, "module.xcconfig") make_xcc = True if os.path.exists(xcconfig): existing_xcc = open(xcconfig).read() # only copy if different so we don't trigger re-compile in xcode make_xcc = existing_xcc != xcconfig_c if make_xcc: xcconfig = open(xcconfig, 'w') xcconfig.write(xcconfig_c) xcconfig.close() #endif deploytype != 'export-build' else: # ... And for exported projects, load export symbols from # the 'metadata' dir. metadata_dir = os.path.join(self.iphone_dir, 'metadata') if os.path.isdir(metadata_dir): for file in os.listdir(metadata_dir): self.load_metadata(os.path.join(metadata_dir, file)) if self.deploytype == 'simulator' or self.deploytype == 'export': shutil.copy(os.path.join(template_dir, 'Classes', 'defines.h'), os.path.join(self.classes_dir, 'defines.h')) if self.deploytype != 'development' or has_modules: if os.path.exists(app_dir) and self.deploytype != 'development': self.copy_resources([resources_dir], app_dir, self.deploytype != 'test', module_js) if self.deploytype == 'production': debugger_plist = os.path.join(app_dir, 'debugger.plist') if os.path.exists(debugger_plist): os.remove(debugger_plist) if self.deploytype != 'development' and self.deploytype != 'export': defines_file = os.path.join(self.classes_dir, 'defines.h') defines_header = open(defines_file, 'w+') defines_content = "// Warning: this is generated file. Do not modify!\n\n" defines_content += "#define TI_VERSION %s\n" % sdk_version for sym in self.defines: defines_content += "#define %s\n" % sym if defines_content != defines_header.read(): defines_header.write(defines_content) defines_header.close() # deploy any module image files for module in self.modules: img_dir = os.path.join(template_dir, 'modules', module.lower(), 'images') print "[DEBUG] module image = %s" % img_dir if not os.path.exists(img_dir): continue dest_img_dir = os.path.join(app_dir, 'modules', module.lower(), 'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) self.copy_resources([img_dir], dest_img_dir, False) if self.deploytype != 'development' and os.path.exists(app_dir): # optimize PNGs - since we don't include them in the Resources of the xcodeproj # the ones we copy in won't get optimized so we need to run it manually # we can skip this on the simulator but should do it on device dev_path = "/Developer" # we need to ask xcode where the root path is path = run.run(["/usr/bin/xcode-select", "-print-path"], True, False) if path: dev_path = path.strip() run.run([ "%s/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize" % dev_path, app_dir ], False) # remove empty directories os.chdir(app_dir) os.system("find . -type d -empty -delete") else: print "[INFO] Skipping JS compile, running from simulator" if self.deploytype == 'development': softlink_for_simulator(self.project_dir, app_dir)
def compileProject(self,xcode=False,devicefamily='ios',iphone_version='iphoneos',silent=False,sdk=None): tiapp_xml = os.path.join(self.project_dir,'tiapp.xml') ti = TiAppXML(tiapp_xml) if sdk is None: sdk_version = os.path.basename(os.path.abspath(os.path.join(template_dir,'../'))) else: sdk_version = sdk if xcode: app_name = os.environ['FULL_PRODUCT_NAME'] app_dir = os.path.join(os.environ['TARGET_BUILD_DIR'],os.environ['CONTENTS_FOLDER_PATH']) else: target = 'Debug' if self.deploytype == 'production': target = 'Release' app_name = self.project_name+'.app' app_folder_name = '%s-iphoneos' % target app_dir = os.path.abspath(os.path.join(self.iphone_dir,'build',app_folder_name,app_name)) if not silent: print "[INFO] Titanium SDK version: %s" % sdk_version print "[INFO] iPhone Device family: %s" % devicefamily print "[INFO] iPhone SDK version: %s" % iphone_version if self.deploytype != 'export-build': main_template_file = os.path.join(template_dir,'main.m') main_template = codecs.open(main_template_file, encoding='utf-8').read() main_template = main_template.replace('__PROJECT_NAME__',self.project_name) main_template = main_template.replace('__PROJECT_ID__',self.appid) main_template = main_template.replace('__DEPLOYTYPE__',self.deploytype) main_template = main_template.replace('__APP_ID__',self.appid) main_template = main_template.replace('__APP_ANALYTICS__',ti.properties['analytics']) main_template = main_template.replace('__APP_PUBLISHER__',ti.properties['publisher']) main_template = main_template.replace('__APP_URL__',ti.properties['url']) main_template = main_template.replace('__APP_NAME__',ti.properties['name']) main_template = main_template.replace('__APP_VERSION__',ti.properties['version']) main_template = main_template.replace('__APP_DESCRIPTION__',ti.properties['description']) main_template = main_template.replace('__APP_COPYRIGHT__',ti.properties['copyright']) main_template = main_template.replace('__APP_GUID__',ti.properties['guid']) main_template = main_template.replace('__APP_RESOURCE_DIR__','') main_template_out = os.path.join(self.iphone_dir,'main.m') main_file = codecs.open(main_template_out,'w+',encoding='utf-8') main_file_contents = main_file.read() if main_file_contents!=main_template: main_file.write(main_template) main_file.close() resources_dir = os.path.join(self.project_dir,'Resources') iphone_resources_dir = os.path.join(resources_dir,'iphone') iphone_platform_dir = os.path.join(self.project_dir,'platform','iphone') # copy in any resources in our module like icons # NOTE: This means that any JS-only modules in the local project # are hashed up and dumped into the export. has_modules = False missing_modules, modules, module_js = ([], [], []) module_js_dir = os.path.join(self.project_dir,'modules') if os.path.exists(module_js_dir): for file in os.listdir(module_js_dir): if file.endswith('.js'): module_js.append({'from':os.path.join(module_js_dir,file),'to':os.path.join(app_dir,file),'path':'modules/'+file}) if self.deploytype != 'export-build': # Have to load the module detection here, in order to # prevent distributing even MORE stuff in export/transport sys.path.append(os.path.join(template_dir,'../module')) from module import ModuleDetector detector = ModuleDetector(self.project_dir) missing_modules, modules = detector.find_app_modules(ti, 'iphone', self.deploytype) # we have to copy these even in simulator given the path difference if os.path.exists(app_dir): self.copy_resources([iphone_resources_dir],app_dir,False) if os.path.exists(app_dir): self.copy_resources([iphone_platform_dir],app_dir,False) # generate the includes for all compiled modules xcconfig_c = "// this is a generated file - DO NOT EDIT\n\n" if len(modules) > 0: mods = open(os.path.join(self.classes_dir,'ApplicationMods.m'),'w+') variables = {} mods.write(MODULE_IMPL_HEADER) for module in modules: if module.js: # CommonJS module module_js.append({'from': module.js, 'path': 'modules/' + os.path.basename(module.js)}) module_id = module.manifest.moduleid.lower() module_name = module.manifest.name.lower() module_version = module.manifest.version module_guid = '' module_licensekey = '' if module.manifest.has_property('guid'): module_guid = module.manifest.guid if module.manifest.has_property('licensekey'): module_licensekey = module.manifest.licensekey self.modules_metadata.append({'guid':module_guid,'name':module_name,'id':module_id,'dir':module.path,'version':module_version,'licensekey':module_licensekey}) xcfile = module.get_resource('module.xcconfig') if os.path.exists(xcfile): xcconfig_contents = parse_xcconfig(xcfile, module_id, variables) xcconfig_c += xcconfig_contents xcfile = os.path.join(self.project_dir,'modules','iphone',"%s.xcconfig" % module_name) if os.path.exists(xcfile): xcconfig_contents = parse_xcconfig(xcfile, module_id, variables) xcconfig_c += xcconfig_contents mods.write(" [modules addObject:[NSDictionary dictionaryWithObjectsAndKeys:@\"%s\",@\"name\",@\"%s\",@\"moduleid\",@\"%s\",@\"version\",@\"%s\",@\"guid\",@\"%s\",@\"licensekey\",nil]];\n" % (module_name,module_id,module_version,module_guid,module_licensekey)); # Load export symbols from modules... metadata_path = os.path.join(module.path, 'metadata.json') if os.path.exists(metadata_path): self.load_metadata(metadata_path) mods.write(" return modules;\n") mods.write("}\n") mods.write(FOOTER) mods.close() for (name, values) in variables.iteritems(): xcconfig_c += name + '=$(inherited) ' for value in values: xcconfig_c += '$(%s) ' % value xcconfig_c += '\n' has_modules = True xcconfig = os.path.join(self.iphone_dir,"module.xcconfig") make_xcc = True if os.path.exists(xcconfig): existing_xcc = open(xcconfig).read() # only copy if different so we don't trigger re-compile in xcode make_xcc = existing_xcc!=xcconfig_c if make_xcc: xcconfig = open(xcconfig,'w') xcconfig.write(xcconfig_c) xcconfig.close() #endif deploytype != 'export-build' else: # ... And for exported projects, load export symbols from # the 'metadata' dir. metadata_dir = os.path.join(self.iphone_dir, 'metadata') if os.path.isdir(metadata_dir): for file in os.listdir(metadata_dir): self.load_metadata(os.path.join(metadata_dir,file)) if self.deploytype=='simulator' or self.deploytype=='export': shutil.copy(os.path.join(template_dir,'Classes','defines.h'),os.path.join(self.classes_dir,'defines.h')) if self.deploytype!='development' or has_modules: if os.path.exists(app_dir) and self.deploytype != 'development': self.copy_resources([resources_dir],app_dir,self.deploytype != 'test',module_js) if self.deploytype == 'production': debugger_plist = os.path.join(app_dir,'debugger.plist') if os.path.exists(debugger_plist): os.remove(debugger_plist) if self.deploytype!='development' and self.deploytype!='export': defines_file = os.path.join(self.classes_dir, 'defines.h') defines_header = open(defines_file,'w+') defines_content = "// Warning: this is generated file. Do not modify!\n\n" defines_content+= "#define TI_VERSION %s\n"%sdk_version for sym in self.defines: defines_content+="#define %s\n" % sym if defines_content!=defines_header.read(): defines_header.write(defines_content) defines_header.close() # deploy any module image files for module in self.modules: img_dir = os.path.join(template_dir,'modules',module.lower(),'images') print "[DEBUG] module image = %s" % img_dir if not os.path.exists(img_dir): continue dest_img_dir = os.path.join(app_dir,'modules',module.lower(),'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) self.copy_resources([img_dir],dest_img_dir,False) if self.deploytype!='development' and os.path.exists(app_dir): # optimize PNGs - since we don't include them in the Resources of the xcodeproj # the ones we copy in won't get optimized so we need to run it manually # we can skip this on the simulator but should do it on device dev_path = "/Developer" # we need to ask xcode where the root path is path = run.run(["/usr/bin/xcode-select","-print-path"],True,False) if path: dev_path = path.strip() run.run(["%s/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize"%dev_path,app_dir],False) # remove empty directories os.chdir(app_dir) os.system("find . -type d -empty -delete") else: print "[INFO] Skipping JS compile, running from simulator" if self.deploytype=='development': softlink_for_simulator(self.project_dir,app_dir)
def build_modules_info(self, resources_dir, app_bin_dir, include_all_ti_modules=False): self.app_modules = [] (modules, external_child_modules) = bindings.get_all_module_bindings() compiler = Compiler(self.tiapp, resources_dir, self.java, app_bin_dir, None, os.path.dirname(app_bin_dir), include_all_modules=include_all_ti_modules) compiler.compile(compile_bytecode=False, info_message=None) for module in compiler.modules: module_bindings = [] # TODO: we should also detect module properties for method in compiler.module_methods: if method.lower().startswith(module + '.') and '.' not in method: module_bindings.append(method[len(module) + 1:]) module_onAppCreate = None module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower() == module: module_class = m module_apiName = modules[m]['fullAPIName'] if 'onAppCreate' in modules[m]: module_onAppCreate = modules[m]['onAppCreate'] break if module_apiName == None: continue # module wasn't found ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[module_class]: if child_module['fullAPIName'].lower() in compiler.modules: ext_modules.append(child_module) self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': module_bindings, 'external_child_modules': ext_modules, 'on_app_create': module_onAppCreate }) # discover app modules detector = ModuleDetector(self.project_dir) missing, detected_modules = detector.find_app_modules( self.tiapp, 'android', self.deploy_type) for missing_module in missing: print '[WARN] Couldn\'t find app module: %s' % missing_module['id'] self.custom_modules = [] for module in detected_modules: if module.jar == None: continue module_jar = zipfile.ZipFile(module.jar) module_bindings = bindings.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings['modules'].keys(): module_apiName = module_bindings['modules'][module_class][ 'apiName'] module_proxy = module_bindings['proxies'][module_class] module_id = module_proxy['proxyAttrs']['id'] module_proxy_class_name = module_proxy['proxyClassName'] module_onAppCreate = None if 'onAppCreate' in module_proxy: module_onAppCreate = module_proxy['onAppCreate'] print '[DEBUG] module_id = %s' % module_id if module_id == module.manifest.moduleid: # make sure that the module was not built before 1.8.0.1 try: module_api_version = int(module.manifest.apiversion) if module_api_version < 2: print "[ERROR] The 'apiversion' for '%s' in the module manifest is less than version 2. The module was likely built against a Titanium SDK pre 1.8.0.1. Please use a version of the module that has 'apiversion' 2 or greater" % module_id touch_tiapp_xml( os.path.join(self.project_dir, 'tiapp.xml')) sys.exit(1) except (TypeError, ValueError): print "[ERROR] The 'apiversion' for '%s' in the module manifest is not a valid value. Please use a version of the module that has an 'apiversion' value of 2 or greater set in it's manifest file" % module_id touch_tiapp_xml( os.path.join(self.project_dir, 'tiapp.xml')) sys.exit(1) is_native_js_module = (hasattr(module.manifest, 'commonjs') and module.manifest.commonjs) print '[DEBUG] appending module: %s' % module_class self.custom_modules.append({ 'module_id': module_id, 'module_apiName': module_apiName, 'proxy_name': module_proxy_class_name, 'class_name': module_class, 'manifest': module.manifest, 'on_app_create': module_onAppCreate, 'is_native_js_module': is_native_js_module }) if is_native_js_module: # Need to look at the app modules used in this external js module metadata_file = os.path.join(module.path, "metadata.json") metadata = None try: f = open(metadata_file, "r") metadata = f.read() finally: f.close() if metadata: metadata = simplejson.loads(metadata) if metadata.has_key("exports"): exported_module_ids = metadata["exports"] already_included_module_ids = [ m["api_name"].lower() for m in self.app_modules ] need_to_add = [ m for m in exported_module_ids if m not in already_included_module_ids ] if need_to_add: for to_add in need_to_add: module_onAppCreate = None module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower( ) == to_add: module_class = m module_apiName = modules[m][ 'fullAPIName'] if 'onAppCreate' in modules[m]: module_onAppCreate = modules[ m]['onAppCreate'] break if module_apiName == None: continue # module wasn't found ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[ module_class]: if child_module[ 'fullAPIName'].lower( ) in compiler.modules: ext_modules.append( child_module) self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': [], 'external_child_modules': ext_modules, 'on_app_create': module_onAppCreate })
def main(args): if len(args) < 2: print "Usage: %s <project_directory> [sdk_verison]" % os.path.basename( args[0]) sys.exit(1) # What needs to be done in order to perform a "true" export? # --- # Wipe the build dir # Migrate resources # Migrate tiapp.xml (required for scripts) # Generate project from template # Populate Info.plist # Compile/migrate i18n # Migrate scripts for compiling JSS files (and i18n) # Modify xcodeproj build steps to call the JSS compiler # Then... Share and Enjoy. project_dir = os.path.abspath(args[1]) build_dir = os.path.join(project_dir, 'build', 'iphone') titanium_local = os.path.join(build_dir, 'titanium') if len(args) == 3: version = args[2] sdk_dir = find_sdk(version) else: sdk_dir = os.path.abspath(os.path.dirname(template_dir)) version = os.path.basename(sdk_dir) tiappxml = os.path.join(project_dir, 'tiapp.xml') tiapp = TiAppXML(tiappxml) app_id = tiapp.properties['id'] app_name = tiapp.properties['name'] if app_id is None or app_name is None: info("Your tiapp.xml is malformed - please specify an app name and id") sys.exit(1) # Clean build dir (if it exists), error otherwise (no iphone support) info("Cleaning build...") if os.path.exists(build_dir): for f in os.listdir(build_dir): path = os.path.join(build_dir, f) if os.path.isfile(path): os.remove(path) else: shutil.rmtree(path) else: info("Your project is not configured to be built for iphone.") exit(1) # Migrate Resources info("Migrating resources...") project_resources = os.path.join(project_dir, 'Resources') resources_dir = os.path.join(build_dir, 'Resources') shutil.copytree(project_resources, resources_dir) # Migrate platform/iphone contents into Resources. info("Migrating platform/iphone to Resources...") project_platform = os.path.join(project_dir, 'platform', 'iphone') if os.path.isdir(project_platform): contents = os.listdir(project_platform) for file in contents: path = os.path.join(project_platform, file) if os.path.isdir(path): shutil.copytree(path, os.path.join(resources_dir, file)) else: shutil.copy(path, os.path.join(resources_dir, file)) # Migrate tiapp.xml info("Migrating tiapp.xml...") shutil.copy(tiappxml, build_dir) # Generate project stuff from the template info("Generating project from Titanium template...") project = Projector(app_name, version, template_dir, project_dir, app_id) project.create(template_dir, build_dir) # Because the debugger.plist is built as part of the required # resources, we need to autogen an empty one debug_plist = os.path.join(resources_dir, 'debugger.plist') force_xcode = write_debugger_plist(None, None, template_dir, debug_plist) # Populate Info.plist applogo = None info("Populating Info.plist...") plist_out = os.path.join(build_dir, 'Info.plist') create_info_plist(tiapp, template_dir, project_dir, plist_out) applogo = tiapp.generate_infoplist(plist_out, app_id, 'iphone', project_dir, None) # Run the compiler to autogenerate .m files info("Copying classes, creating generated .m files...") compiler = Compiler(project_dir, app_id, app_name, 'export') compiler.compileProject(silent=True) #... But we still have to nuke the stuff that gets built that we don't want # to bundle. ios_build = os.path.join(build_dir, 'build') if os.path.isdir(ios_build): shutil.rmtree(os.path.join(build_dir, 'build')) # Install applogo/splash/etc. info("Copying icons and splash...") install_logo(tiapp, applogo, project_dir, template_dir, resources_dir) install_defaults(project_dir, template_dir, resources_dir) # Get Modules detector = ModuleDetector(project_dir) missing_modules, modules = detector.find_app_modules(tiapp, 'iphone') if len(missing_modules) != 0: for module in missing_modules: info("MISSING MODULE: %s ... Project will not build correctly" % module['id']) info("Terminating export: Please fix your modules.") sys.exit(1) module_search_path, module_asset_dirs = locate_modules( modules, project_dir, resources_dir, info) lib_dir = os.path.join(build_dir, 'lib') if not os.path.exists(lib_dir): os.makedirs(lib_dir) if len(module_search_path) > 0: info("Copying modules...") for module in module_search_path: module_name, module_path = module info("\t%s..." % module_name) shutil.copy(os.path.join(module_path, module_name), lib_dir) module[1] = os.path.join(lib_dir, module_name) info("Copying module metadata...") metadata_dir = os.path.join(build_dir, 'metadata') for module in modules: module_metadata = os.path.join(module.path, 'metadata.json') if os.path.exists(module_metadata): if not os.path.exists(metadata_dir): os.makedirs(metadata_dir) target = os.path.join(metadata_dir, "%s.json" % module.manifest.moduleid) shutil.copyfile(module_metadata, target) # Note: The module link information has to be added to # the xcodeproj after it's created. # We also have to mod the module_search_path to reference # the local 'lib' directory instead of the original # module install location info("Linking modules...") local_modules = [] for module in module_search_path: name = module[0] newpath = os.path.join('lib', name) local_modules.append([name, newpath]) link_modules(local_modules, app_name, build_dir, relative=True) # Copy libraries info("Copying libraries...") iphone_dir = os.path.join(sdk_dir, 'iphone') for lib in glob.iglob(os.path.join(iphone_dir, 'lib*')): info("\t%s..." % lib) shutil.copy(lib, lib_dir) # Process i18n files info("Processing i18n...") locale_compiler = LocaleCompiler(app_name, project_dir, 'ios', 'development', resources_dir) locale_compiler.compile() # Migrate compile scripts info("Copying custom Titanium compiler scripts...") shutil.copytree(os.path.join(sdk_dir, 'common'), titanium_local) shutil.copy(os.path.join(sdk_dir, 'tiapp.py'), titanium_local) iphone_script_dir = os.path.join(titanium_local, 'iphone') os.mkdir(iphone_script_dir) shutil.copy(os.path.join(sdk_dir, 'iphone', 'compiler.py'), iphone_script_dir) shutil.copy(os.path.join(sdk_dir, 'iphone', 'tools.py'), iphone_script_dir) shutil.copy(os.path.join(sdk_dir, 'iphone', 'run.py'), iphone_script_dir) shutil.copy(os.path.join(sdk_dir, 'iphone', 'csspacker.py'), iphone_script_dir) shutil.copy(os.path.join(sdk_dir, 'iphone', 'jspacker.py'), iphone_script_dir) shutil.copy(os.path.join(sdk_dir, 'iphone', 'titanium_prep'), iphone_script_dir) # Add compilation to the build script in project info("Modifying pre-compile stage...") xcodeproj = os.path.join(build_dir, '%s.xcodeproj' % app_name, 'project.pbxproj') contents = codecs.open(xcodeproj, 'r', encoding='utf-8').read() css_compiler = os.path.join('titanium', 'css', 'csscompiler.py') ti_compiler = os.path.join('titanium', 'iphone', 'compiler.py') script = """%s . ios Resources %s . export-build $TARGETED_DEVICE_FAMILY $SDKROOT %s""" % ( css_compiler, ti_compiler, version) contents = fix_xcode_script(contents, "Pre-Compile", script) # write our new project f = codecs.open(xcodeproj, 'w', encoding='utf-8') f.write(contents) f.close() info("Finished! Share and Enjoy.")
def build_modules_info(self, resources_dir, app_bin_dir, include_all_ti_modules=False): self.app_modules = [] (modules, external_child_modules) = bindings.get_all_module_bindings() compiler = Compiler(self.tiapp, resources_dir, self.java, app_bin_dir, None, os.path.dirname(app_bin_dir), include_all_modules=include_all_ti_modules) compiler.compile(compile_bytecode=False, info_message=None) for module in compiler.modules: module_bindings = [] # TODO: we should also detect module properties for method in compiler.module_methods: if method.lower().startswith(module+'.') and '.' not in method: module_bindings.append(method[len(module)+1:]) module_onAppCreate = None module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower() == module: module_class = m module_apiName = modules[m]['fullAPIName'] if 'onAppCreate' in modules[m]: module_onAppCreate = modules[m]['onAppCreate'] break if module_apiName == None: continue # module wasn't found ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[module_class]: if child_module['fullAPIName'].lower() in compiler.modules: ext_modules.append(child_module) self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': module_bindings, 'external_child_modules': ext_modules, 'on_app_create': module_onAppCreate }) # discover app modules detector = ModuleDetector(self.project_dir) missing, detected_modules = detector.find_app_modules(self.tiapp, 'android', self.deploy_type) for missing_module in missing: print '[WARN] Couldn\'t find app module: %s' % missing_module['id'] self.custom_modules = [] for module in detected_modules: if module.jar == None: continue module_jar = zipfile.ZipFile(module.jar) module_bindings = bindings.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings['modules'].keys(): module_apiName = module_bindings['modules'][module_class]['apiName'] module_proxy = module_bindings['proxies'][module_class] module_id = module_proxy['proxyAttrs']['id'] module_proxy_class_name = module_proxy['proxyClassName'] module_onAppCreate = None if 'onAppCreate' in module_proxy: module_onAppCreate = module_proxy['onAppCreate'] print '[DEBUG] module_id = %s' % module_id if module_id == module.manifest.moduleid: # make sure that the module was not built before 1.8.0.1 try: module_api_version = int(module.manifest.apiversion) if module_api_version < 2: print "[ERROR] The 'apiversion' for '%s' in the module manifest is less than version 2. The module was likely built against a Titanium SDK pre 1.8.0.1. Please use a version of the module that has 'apiversion' 2 or greater" % module_id touch_tiapp_xml(os.path.join(self.project_dir, 'tiapp.xml')) sys.exit(1) except(TypeError, ValueError): print "[ERROR] The 'apiversion' for '%s' in the module manifest is not a valid value. Please use a version of the module that has an 'apiversion' value of 2 or greater set in it's manifest file" % module_id touch_tiapp_xml(os.path.join(self.project_dir, 'tiapp.xml')) sys.exit(1) is_native_js_module = (hasattr(module.manifest, 'commonjs') and module.manifest.commonjs) print '[DEBUG] appending module: %s' % module_class self.custom_modules.append({ 'module_id': module_id, 'module_apiName': module_apiName, 'proxy_name': module_proxy_class_name, 'class_name': module_class, 'manifest': module.manifest, 'on_app_create': module_onAppCreate, 'is_native_js_module': is_native_js_module }) if is_native_js_module: # Need to look at the app modules used in this external js module metadata_file = os.path.join(module.path, "metadata.json") metadata = None try: f = open(metadata_file, "r") metadata = f.read() finally: f.close() if metadata: metadata = simplejson.loads(metadata) if metadata.has_key("exports"): exported_module_ids = metadata["exports"] already_included_module_ids = [m["api_name"].lower() for m in self.app_modules] need_to_add = [m for m in exported_module_ids if m not in already_included_module_ids] if need_to_add: for to_add in need_to_add: module_onAppCreate = None module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower() == to_add: module_class = m module_apiName = modules[m]['fullAPIName'] if 'onAppCreate' in modules[m]: module_onAppCreate = modules[m]['onAppCreate'] break if module_apiName == None: continue # module wasn't found ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[module_class]: if child_module['fullAPIName'].lower() in compiler.modules: ext_modules.append(child_module) self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': [], 'external_child_modules': ext_modules, 'on_app_create': module_onAppCreate })
def compileProject(self, xcode=False, devicefamily="ios", iphone_version="iphoneos", silent=False, sdk=None): tiapp_xml = os.path.join(self.project_dir, "tiapp.xml") ti = TiAppXML(tiapp_xml) if sdk is None: sdk_version = os.path.basename(os.path.abspath(os.path.join(template_dir, "../"))) else: sdk_version = sdk if xcode: app_name = os.environ["FULL_PRODUCT_NAME"] app_dir = os.path.join(os.environ["TARGET_BUILD_DIR"], os.environ["CONTENTS_FOLDER_PATH"]) else: target = "Debug" if self.deploytype == "production": target = "Release" app_name = self.project_name + ".app" app_folder_name = "%s-iphoneos" % target app_dir = os.path.abspath(os.path.join(self.iphone_dir, "build", app_folder_name, app_name)) if not silent: print "[INFO] Titanium SDK version: %s" % sdk_version print "[INFO] iPhone Device family: %s" % devicefamily print "[INFO] iPhone SDK version: %s" % iphone_version if self.deploytype != "export-build": main_template_file = os.path.join(template_dir, "main.m") main_template = codecs.open(main_template_file, encoding="utf-8").read() main_template = main_template.replace("__PROJECT_NAME__", self.project_name) main_template = main_template.replace("__PROJECT_ID__", self.appid) main_template = main_template.replace("__DEPLOYTYPE__", self.deploytype) main_template = main_template.replace("__APP_ID__", self.appid) main_template = main_template.replace("__APP_ANALYTICS__", ti.properties["analytics"]) main_template = main_template.replace("__APP_PUBLISHER__", ti.properties["publisher"]) main_template = main_template.replace("__APP_URL__", ti.properties["url"]) main_template = main_template.replace("__APP_NAME__", ti.properties["name"]) main_template = main_template.replace("__APP_VERSION__", ti.properties["version"]) main_template = main_template.replace("__APP_DESCRIPTION__", ti.properties["description"]) main_template = main_template.replace("__APP_COPYRIGHT__", ti.properties["copyright"]) main_template = main_template.replace("__APP_GUID__", ti.properties["guid"]) main_template = main_template.replace("__APP_RESOURCE_DIR__", "") main_template_out = os.path.join(self.iphone_dir, "main.m") main_file = codecs.open(main_template_out, "w+", encoding="utf-8") main_file_contents = main_file.read() if main_file_contents != main_template: main_file.write(main_template) main_file.close() resources_dir = os.path.join(self.project_dir, "Resources") iphone_resources_dir = os.path.join(resources_dir, "iphone") iphone_platform_dir = os.path.join(self.project_dir, "platform", "iphone") # copy in any resources in our module like icons # NOTE: This means that any JS-only modules in the local project # are hashed up and dumped into the export. has_modules = False missing_modules, modules, module_js = ([], [], []) module_js_dir = os.path.join(self.project_dir, "modules") if os.path.exists(module_js_dir): for file in os.listdir(module_js_dir): if file.endswith(".js"): module_js.append( { "from": os.path.join(module_js_dir, file), "to": os.path.join(app_dir, file), "path": "modules/" + file, } ) if self.deploytype != "export-build": # Have to load the module detection here, in order to # prevent distributing even MORE stuff in export/transport sys.path.append(os.path.join(template_dir, "../module")) from module import ModuleDetector detector = ModuleDetector(self.project_dir) missing_modules, modules = detector.find_app_modules(ti, "iphone") # we have to copy these even in simulator given the path difference if os.path.exists(app_dir): self.copy_resources([iphone_resources_dir], app_dir, False) if os.path.exists(app_dir): self.copy_resources([iphone_platform_dir], app_dir, False) # generate the includes for all compiled modules xcconfig_c = "// this is a generated file - DO NOT EDIT\n\n" if len(modules) > 0: mods = open(os.path.join(self.classes_dir, "ApplicationMods.m"), "w+") variables = {} mods.write(MODULE_IMPL_HEADER) for module in modules: if module.js: # CommonJS module module_js.append({"from": module.js, "path": "modules/" + os.path.basename(module.js)}) module_id = module.manifest.moduleid.lower() module_name = module.manifest.name.lower() module_version = module.manifest.version module_guid = "" module_licensekey = "" if module.manifest.has_property("guid"): module_guid = module.manifest.guid if module.manifest.has_property("licensekey"): module_licensekey = module.manifest.licensekey self.modules_metadata.append( { "guid": module_guid, "name": module_name, "id": module_id, "dir": module.path, "version": module_version, "licensekey": module_licensekey, } ) xcfile = module.get_resource("module.xcconfig") if os.path.exists(xcfile): xcconfig_contents = parse_xcconfig(xcfile, module_id, variables) xcconfig_c += xcconfig_contents xcfile = os.path.join(self.project_dir, "modules", "iphone", "%s.xcconfig" % module_name) if os.path.exists(xcfile): xcconfig_contents = parse_xcconfig(xcfile, module_id, variables) xcconfig_c += xcconfig_contents mods.write( ' [modules addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"%s",@"name",@"%s",@"moduleid",@"%s",@"version",@"%s",@"guid",@"%s",@"licensekey",nil]];\n' % (module_name, module_id, module_version, module_guid, module_licensekey) ) # Load export symbols from modules... metadata_path = os.path.join(module.path, "metadata.json") if os.path.exists(metadata_path): self.load_metadata(metadata_path) mods.write(" return modules;\n") mods.write("}\n") mods.write(FOOTER) mods.close() for (name, values) in variables.iteritems(): xcconfig_c += name + "=$(inherited) " for value in values: xcconfig_c += "$(%s) " % value xcconfig_c += "\n" has_modules = True xcconfig = os.path.join(self.iphone_dir, "module.xcconfig") make_xcc = True if os.path.exists(xcconfig): existing_xcc = open(xcconfig).read() # only copy if different so we don't trigger re-compile in xcode make_xcc = existing_xcc != xcconfig_c if make_xcc: xcconfig = open(xcconfig, "w") xcconfig.write(xcconfig_c) xcconfig.close() # endif deploytype != 'export-build' else: # ... And for exported projects, load export symbols from # the 'metadata' dir. metadata_dir = os.path.join(self.iphone_dir, "metadata") if os.path.isdir(metadata_dir): for file in os.listdir(metadata_dir): self.load_metadata(os.path.join(metadata_dir, file)) if self.deploytype == "simulator" or self.deploytype == "export": shutil.copy(os.path.join(template_dir, "Classes", "defines.h"), os.path.join(self.classes_dir, "defines.h")) if self.deploytype != "development" or has_modules: if os.path.exists(app_dir) and self.deploytype != "development": self.copy_resources([resources_dir], app_dir, True, module_js) if self.deploytype == "production": debugger_plist = os.path.join(app_dir, "debugger.plist") if os.path.exists(debugger_plist): os.remove(debugger_plist) if self.deploytype != "development" and self.deploytype != "export": defines_file = os.path.join(self.classes_dir, "defines.h") defines_header = open(defines_file, "w+") defines_content = "// Warning: this is generated file. Do not modify!\n\n" defines_content += "#define TI_VERSION %s\n" % sdk_version for sym in self.defines: defines_content += "#define %s\n" % sym if defines_content != defines_header.read(): defines_header.write(defines_content) defines_header.close() # deploy any module image files for module in self.modules: img_dir = os.path.join(template_dir, "modules", module.lower(), "images") print "[DEBUG] module image = %s" % img_dir if not os.path.exists(img_dir): continue dest_img_dir = os.path.join(app_dir, "modules", module.lower(), "images") if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) self.copy_resources([img_dir], dest_img_dir, False) if self.deploytype != "development" and os.path.exists(app_dir): # optimize PNGs - since we don't include them in the Resources of the xcodeproj # the ones we copy in won't get optimized so we need to run it manually # we can skip this on the simulator but should do it on device dev_path = "/Developer" # we need to ask xcode where the root path is path = run.run(["/usr/bin/xcode-select", "-print-path"], True, False) if path: dev_path = path.strip() run.run( ["%s/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize" % dev_path, app_dir], False ) # remove empty directories os.chdir(app_dir) os.system("find . -type d -empty -delete") else: print "[INFO] Skipping JS compile, running from simulator" if self.deploytype == "development": softlink_for_simulator(self.project_dir, app_dir)
def __init__(self, project_dir, appid, name, deploytype, xcode, devicefamily, iphone_version, silent=False): self.project_dir = project_dir self.project_name = name self.appid = appid self.iphone_dir = os.path.join(project_dir, 'build', 'iphone') self.classes_dir = os.path.join(self.iphone_dir, 'Classes') self.modules = [] self.modules_metadata = [] # for now, these are required self.defines = [ 'USE_TI_ANALYTICS', 'USE_TI_NETWORK', 'USE_TI_PLATFORM', 'USE_TI_UI' ] tiapp_xml = os.path.join(project_dir, 'tiapp.xml') ti = TiAppXML(tiapp_xml) sdk_version = os.path.basename( os.path.abspath(os.path.join(template_dir, '../'))) detector = ModuleDetector(project_dir) missing_modules, modules = detector.find_app_modules(ti, 'iphone') if xcode: app_name = os.environ['FULL_PRODUCT_NAME'] app_dir = os.path.join(os.environ['TARGET_BUILD_DIR'], os.environ['CONTENTS_FOLDER_PATH']) else: target = 'Debug' if deploytype == 'install': target = 'Release' app_name = name + '.app' app_folder_name = '%s-iphoneos' % target app_dir = os.path.abspath( os.path.join(self.iphone_dir, 'build', app_folder_name, app_name)) main_template_file = os.path.join(template_dir, 'main.m') main_template = codecs.open(main_template_file, encoding='utf-8').read() main_template = main_template.replace('__PROJECT_NAME__', name) main_template = main_template.replace('__PROJECT_ID__', appid) main_template = main_template.replace('__DEPLOYTYPE__', deploytype) main_template = main_template.replace('__APP_ID__', appid) main_template = main_template.replace('__APP_ANALYTICS__', ti.properties['analytics']) main_template = main_template.replace('__APP_PUBLISHER__', ti.properties['publisher']) main_template = main_template.replace('__APP_URL__', ti.properties['url']) main_template = main_template.replace('__APP_NAME__', ti.properties['name']) main_template = main_template.replace('__APP_VERSION__', ti.properties['version']) main_template = main_template.replace('__APP_DESCRIPTION__', ti.properties['description']) main_template = main_template.replace('__APP_COPYRIGHT__', ti.properties['copyright']) main_template = main_template.replace('__APP_GUID__', ti.properties['guid']) if deploytype == 'development': main_template = main_template.replace( '__APP_RESOURCE_DIR__', os.path.abspath(os.path.join(project_dir, 'Resources'))) else: main_template = main_template.replace('__APP_RESOURCE_DIR__', '') if not silent: print "[INFO] Titanium SDK version: %s" % sdk_version print "[INFO] iPhone Device family: %s" % devicefamily print "[INFO] iPhone SDK version: %s" % iphone_version main_template_out = os.path.join(self.iphone_dir, 'main.m') main_file = codecs.open(main_template_out, 'w+', encoding='utf-8') main_file_contents = main_file.read() if main_file_contents != main_template: main_file.write(main_template) main_file.close() if deploytype == 'production': version = ti.properties['version'] # we want to make sure in debug mode the version always changes version = "%s.%d" % (version, time.time()) ti.properties['version'] = version resources_dir = os.path.join(project_dir, 'Resources') iphone_resources_dir = os.path.join(resources_dir, 'iphone') # copy in any resources in our module like icons project_module_dir = os.path.join(project_dir, 'modules', 'iphone') if os.path.exists(project_module_dir): self.copy_resources([project_module_dir], app_dir, False) # we have to copy these even in simulator given the path difference if os.path.exists(app_dir): self.copy_resources([iphone_resources_dir], app_dir, False) # generate the includes for all compiled modules xcconfig_c = "// this is a generated file - DO NOT EDIT\n\n" has_modules = False if len(modules) > 0: mods = open(os.path.join(self.classes_dir, 'ApplicationMods.m'), 'w+') mods.write(MODULE_IMPL_HEADER) for module in modules: module_id = module.manifest.moduleid.lower() module_name = module.manifest.name.lower() module_version = module.manifest.version module_guid = '' module_licensekey = '' if module.manifest.has_property('guid'): module_guid = module.manifest.guid if module.manifest.has_property('licensekey'): module_licensekey = module.manifest.licensekey self.modules_metadata.append({ 'guid': module_guid, 'name': module_name, 'id': module_id, 'dir': module.path, 'version': module_version, 'licensekey': module_licensekey }) xcfile = module.get_resource('module.xcconfig') if os.path.exists(xcfile): xcconfig_c += "#include \"%s\"\n" % xcfile xcfile = os.path.join(self.project_dir, 'modules', 'iphone', "%s.xcconfig" % module_name) if os.path.exists(xcfile): xcconfig_c += "#include \"%s\"\n" % xcfile mods.write( " [modules addObject:[NSDictionary dictionaryWithObjectsAndKeys:@\"%s\",@\"name\",@\"%s\",@\"moduleid\",@\"%s\",@\"version\",@\"%s\",@\"guid\",@\"%s\",@\"licensekey\",nil]];\n" % (module_name, module_id, module_version, module_guid, module_licensekey)) mods.write(" return modules;\n") mods.write("}\n") mods.write(FOOTER) mods.close() has_modules = True xcconfig = os.path.join(self.iphone_dir, "module.xcconfig") make_xcc = True if os.path.exists(xcconfig): existing_xcc = open(xcconfig).read() # only copy if different so we don't trigger re-compile in xcode make_xcc = existing_xcc != xcconfig_c if make_xcc: xcconfig = open(xcconfig, 'w') xcconfig.write(xcconfig_c) xcconfig.close() if deploytype == 'simulator': shutil.copy(os.path.join(template_dir, 'Classes', 'defines.h'), os.path.join(self.classes_dir, 'defines.h')) if deploytype != 'development' or has_modules: if os.path.exists(app_dir): self.copy_resources([resources_dir], app_dir) if deploytype != 'development': defines_file = os.path.join(self.classes_dir, 'defines.h') defines_header = open(defines_file, 'w+') defines_content = "// Warning: this is generated file. Do not modify!\n\n" defines_content += "#define TI_VERSION %s\n" % sdk_version for sym in self.defines: defines_content += "#define %s 1\n" % sym if defines_content != defines_header.read(): defines_header.write(defines_content) defines_header.close() # deploy any module image files for module in self.modules: img_dir = os.path.join(template_dir, 'modules', module.lower(), 'images') print "[DEBUG] module image = %s" % img_dir if not os.path.exists(img_dir): continue dest_img_dir = os.path.join(app_dir, 'modules', module.lower(), 'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) self.copy_resources([img_dir], dest_img_dir, False) if deploytype != 'development' and os.path.exists(app_dir): # optimize PNGs - since we don't include them in the Resources of the xcodeproj # the ones we copy in won't get optimized so we need to run it manually # we can skip this on the simulator but should do it on device dev_path = "/Developer" # we need to ask xcode where the root path is path = run.run(["/usr/bin/xcode-select", "-print-path"], True, False) if path: dev_path = path.strip() run.run([ "%s/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize" % dev_path, app_dir ], False) # remove empty directories os.chdir(app_dir) os.system("find . -type d -empty -delete") else: print "[INFO] Skipping JS compile, running from simulator"
def main(args): global script_ok argc = len(args) if argc < 2 or argc==2 and (args[1]=='--help' or args[1]=='-h'): print "%s <command> <version> <project_dir> <appid> <name> [options]" % os.path.basename(args[0]) print print "available commands: " print print " install install the app to itunes for testing on iphone" print " simulator build and run on the iphone simulator" print " distribute build final distribution bundle" print " xcode build from within xcode" print " run build and run app from project folder" sys.exit(1) print "[INFO] One moment, building ..." sys.stdout.flush() start_time = time.time() command = args[1].decode("utf-8") target = 'Debug' deploytype = 'development' devicefamily = 'iphone' debug = False simulator = False xcode_build = False force_xcode = False simtype = devicefamily # when you run from xcode, we'll pass xcode as the command and the # xcode script will simply pass some additional args as well as xcode # will add some additional useful stuff to the ENVIRONMENT and we pull # those values out here if command == 'xcode': xcode_build = True src_root = os.environ['SOURCE_ROOT'] project_dir = os.path.abspath(os.path.join(src_root,'../','../')) name = os.environ['PROJECT_NAME'] target = os.environ['CONFIGURATION'] appid = os.environ['TI_APPID'] arch = os.environ['CURRENT_ARCH'] sdk_name = os.environ['SDK_NAME'] iphone_version = sdk_name.replace('iphoneos','').replace('iphonesimulator','') # SUPPORTED_DEVICE_FAMILIES 1 or 2 or both # TARGETED_DEVICE_FAMILY 1 or 2 target_device = os.environ['TARGETED_DEVICE_FAMILY'] if target_device == '1': devicefamily = 'iphone' elif target_device == '2': devicefamily = 'ipad' elif target_device == '1,2': devicefamily = 'universal' if arch == 'i386': # simulator always indicates simulator deploytype = 'development' else: # if arch!=i386 indicates a build for device if target=='Debug': # non-simulator + debug build indicates test on device deploytype = 'test' else: # non-simulator + release build indicates package for distribution deploytype = 'production' compiler = Compiler(project_dir,appid,name,deploytype,xcode_build,devicefamily,iphone_version) script_ok = True sys.exit(0) else: # the run command is when you run from titanium using the run command # and it will run the project in the current directory immediately in the simulator # from the command line if command == 'run': if argc < 3: print "Usage: %s run <project_dir> [ios_version]" % os.path.basename(args[0]) sys.exit(1) if argc == 3: iphone_version = check_iphone_sdk('4.0') else: iphone_version = dequote(args[3].decode("utf-8")) project_dir = os.path.expanduser(dequote(args[2].decode("utf-8"))) iphonesim = os.path.abspath(os.path.join(template_dir,'iphonesim')) iphone_dir = os.path.abspath(os.path.join(project_dir,'build','iphone')) tiapp_xml = os.path.join(project_dir,'tiapp.xml') ti = TiAppXML(tiapp_xml) appid = ti.properties['id'] name = ti.properties['name'] command = 'simulator' # switch it so that the rest of the stuff works else: iphone_version = dequote(args[2].decode("utf-8")) iphonesim = os.path.abspath(os.path.join(template_dir,'iphonesim')) project_dir = os.path.expanduser(dequote(args[3].decode("utf-8"))) appid = dequote(args[4].decode("utf-8")) name = dequote(args[5].decode("utf-8")) tiapp_xml = os.path.join(project_dir,'tiapp.xml') ti = TiAppXML(tiapp_xml) app_name = make_app_name(name) iphone_dir = os.path.abspath(os.path.join(project_dir,'build','iphone')) project_xcconfig = os.path.join(iphone_dir,'project.xcconfig') target = 'Release' ostype = 'os' version_file = None log_id = None provisioning_profile = None # starting in 1.4, you don't need to actually keep the build/iphone directory # if we don't find it, we'll just simply re-generate it if not os.path.exists(iphone_dir): from iphone import IPhone print "[INFO] Detected missing project but that's OK. re-creating it..." iphone_creator = IPhone(name,appid) iphone_creator.create(iphone_dir,True) sys.stdout.flush() # we use different arguments dependent on the command # pluck those out here if command == 'distribute': iphone_version = check_iphone_sdk(iphone_version) link_version = iphone_version appuuid = dequote(args[6].decode("utf-8")) dist_name = dequote(args[7].decode("utf-8")) output_dir = os.path.expanduser(dequote(args[8].decode("utf-8"))) if argc > 9: devicefamily = dequote(args[9].decode("utf-8")) deploytype = 'production' elif command == 'simulator': link_version = check_iphone_sdk(iphone_version) deploytype = 'development' debug = True simulator = True target = 'Debug' ostype = 'simulator' if argc > 6: devicefamily = dequote(args[6].decode("utf-8")) if argc > 7: simtype = dequote(args[7].decode("utf-8")) else: # 'universal' helpfully translates into iPhone here... just in case. simtype = devicefamily elif command == 'install': iphone_version = check_iphone_sdk(iphone_version) link_version = iphone_version appuuid = dequote(args[6].decode("utf-8")) dist_name = dequote(args[7].decode("utf-8")) if argc > 8: devicefamily = dequote(args[8].decode("utf-8")) deploytype = 'test' # setup up the useful directories we need in the script build_out_dir = os.path.abspath(os.path.join(iphone_dir,'build')) build_dir = os.path.abspath(os.path.join(build_out_dir,'%s-iphone%s'%(target,ostype))) app_dir = os.path.abspath(os.path.join(build_dir,name+'.app')) binary = os.path.join(app_dir,name) sdk_version = os.path.basename(os.path.abspath(os.path.join(template_dir,'../'))) iphone_resources_dir = os.path.join(iphone_dir,'Resources') version_file = os.path.join(iphone_resources_dir,'.version') force_rebuild = read_project_version(project_xcconfig)!=sdk_version or not os.path.exists(version_file) infoplist = os.path.join(iphone_dir,'Info.plist') githash = None custom_fonts = [] # if we're not running in the simulator we want to clean out the build directory if command!='simulator' and os.path.exists(build_out_dir): shutil.rmtree(build_out_dir) if not os.path.exists(build_out_dir): os.makedirs(build_out_dir) # write out the build log, useful for debugging o = codecs.open(os.path.join(build_out_dir,'build.log'),'w',encoding='utf-8') def log(msg): print msg o.write(msg) try: buildtime = datetime.datetime.now() o.write("%s\n" % ("="*80)) o.write("Appcelerator Titanium Diagnostics Build Log\n") o.write("The contents of this file are useful to send to Appcelerator Support if\n") o.write("reporting an issue to help us understand your environment, build settings\n") o.write("and aid in debugging. Please attach this log to any issue that you report.\n") o.write("%s\n\n" % ("="*80)) o.write("Starting build at %s\n\n" % buildtime.strftime("%m/%d/%y %H:%M")) # write out the build versions info versions_txt = read_config(os.path.join(template_dir,'..','version.txt')) o.write("Build details:\n\n") for key in versions_txt: o.write(" %s=%s\n" % (key,versions_txt[key])) o.write("\n\n") if versions_txt.has_key('githash'): githash = versions_txt['githash'] o.write("Script arguments:\n") for arg in args: o.write(unicode(" %s\n" % arg, 'utf-8')) o.write("\n") o.write("Building from: %s\n" % template_dir) o.write("Platform: %s\n\n" % platform.version()) # print out path to debug xcode_path=run.run(["/usr/bin/xcode-select","-print-path"],True,False) if xcode_path: o.write("Xcode path is: %s\n" % xcode_path) else: o.write("Xcode path undetermined\n") # find the module directory relative to the root of the SDK titanium_dir = os.path.abspath(os.path.join(template_dir,'..','..','..','..')) tp_module_dir = os.path.abspath(os.path.join(titanium_dir,'modules','iphone')) force_destroy_build = command!='simulator' detector = ModuleDetector(project_dir) missing_modules, modules = detector.find_app_modules(ti, 'iphone') module_lib_search_path = [] module_asset_dirs = [] # search for modules that the project is using # and make sure we add them to the compile for module in modules: module_id = module.manifest.moduleid.lower() module_version = module.manifest.version module_lib_name = 'lib%s.a' % module_id # check first in the local project local_module_lib = os.path.join(project_dir, 'modules', 'iphone', module_lib_name) local = False if os.path.exists(local_module_lib): module_lib_search_path.append([module_lib_name, local_module_lib]) local = True log("[INFO] Detected third-party module: %s" % (local_module_lib)) else: if module.lib is None: module_lib_path = module.get_resource(module_lib_name) log("[ERROR] Third-party module: %s/%s missing library at %s" % (module_id, module_version, module_lib_path)) sys.exit(1) module_lib_search_path.append([module_lib_name, os.path.abspath(module.lib)]) log("[INFO] Detected third-party module: %s/%s" % (module_id, module_version)) force_xcode = True if not local: # copy module resources img_dir = module.get_resource('assets', 'images') if os.path.exists(img_dir): dest_img_dir = os.path.join(app_dir, 'modules', module_id, 'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) module_asset_dirs.append([img_dir, dest_img_dir]) # copy in any module assets module_assets_dir = module.get_resource('assets') if os.path.exists(module_assets_dir): module_dir = os.path.join(app_dir, 'modules', module_id) module_asset_dirs.append([module_assets_dir, module_dir]) print "[INFO] Titanium SDK version: %s" % sdk_version print "[INFO] iPhone Device family: %s" % devicefamily print "[INFO] iPhone SDK version: %s" % iphone_version if simulator: print "[INFO] iPhone simulated device: %s" % simtype # during simulator we need to copy in standard built-in module files # since we might not run the compiler on subsequent launches for module_name in ('facebook','ui'): img_dir = os.path.join(template_dir,'modules',module_name,'images') dest_img_dir = os.path.join(app_dir,'modules',module_name,'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) module_asset_dirs.append([img_dir,dest_img_dir]) # when in simulator since we point to the resources directory, we need # to explicitly copy over any files ird = os.path.join(project_dir,'Resources','iphone') if os.path.exists(ird): module_asset_dirs.append([ird,app_dir]) for ext in ('ttf','otf'): for f in glob.glob('%s/*.%s' % (os.path.join(project_dir,'Resources'),ext)): custom_fonts.append(f) if not simulator: version = ti.properties['version'] # we want to make sure in debug mode the version always changes version = "%s.%d" % (version,time.time()) ti.properties['version']=version pp = os.path.expanduser("~/Library/MobileDevice/Provisioning Profiles/%s.mobileprovision" % appuuid) provisioning_profile = read_provisioning_profile(pp,o) # TODO: # This code is used elsewhere, as well. We should move stuff like this to # a common file. def write_info_plist(infoplist_tmpl): plist = codecs.open(infoplist_tmpl, encoding='utf-8').read() plist = plist.replace('__PROJECT_NAME__',name) plist = plist.replace('__PROJECT_ID__',appid) plist = plist.replace('__URL__',appid) urlscheme = name.replace('.','_').replace(' ','').lower() plist = plist.replace('__URLSCHEME__',urlscheme) if ti.has_app_property('ti.facebook.appid'): fbid = ti.get_app_property('ti.facebook.appid') plist = plist.replace('__ADDITIONAL_URL_SCHEMES__', '<string>fb%s</string>' % fbid) else: plist = plist.replace('__ADDITIONAL_URL_SCHEMES__','') pf = codecs.open(infoplist,'w', encoding='utf-8') pf.write(plist) pf.close() # if the user has a Info.plist in their project directory, consider # that a custom override infoplist_tmpl = os.path.join(project_dir,'Info.plist') if os.path.exists(infoplist_tmpl): shutil.copy(infoplist_tmpl,infoplist) else: infoplist_tmpl = os.path.join(template_dir,'Info.plist') write_info_plist(infoplist_tmpl) applogo = None clean_build = False # check to see if the appid is different (or not specified) - we need to re-generate if read_project_appid(project_xcconfig)!=appid or not infoplist_has_appid(infoplist,appid): clean_build = True force_xcode = True new_lib_hash = None lib_hash = None existing_git_hash = None # this code simply tries and detect if we're building a different # version of the project (or same version but built from different git hash) # and if so, make sure we force rebuild so to propograte any code changes in # source code (either upgrade or downgrade) if os.path.exists(app_dir): if os.path.exists(version_file): line = open(version_file).read().strip() lines = line.split(",") v = lines[0] log_id = lines[1] if len(lines) > 2: lib_hash = lines[2] existing_git_hash = lines[3] if lib_hash==None: force_rebuild = True else: if template_dir==v and force_rebuild==False: force_rebuild = False else: log_id = None else: force_rebuild = True else: force_rebuild = True o.write("\ngithash=%s, existing_git_hash=%s\n" %(githash,existing_git_hash)) if githash!=existing_git_hash: force_rebuild = True # we want to read the md5 of the libTiCore.a library since it must match # the current one we're building and if not, we need to force a rebuild since # that means we've copied in a different version of the library and we need # to rebuild clean to avoid linking errors source_lib=os.path.join(template_dir,'libTiCore.a') fd = open(source_lib,'rb') m = hashlib.md5() m.update(fd.read(1024)) # just read 1K, it's binary new_lib_hash = m.hexdigest() fd.close() if new_lib_hash!=lib_hash: force_rebuild=True o.write("forcing rebuild since libhash (%s) not matching (%s)\n" % (lib_hash,new_lib_hash)) lib_hash=new_lib_hash # when we force rebuild, we need to re-compile and re-copy source, libs etc if force_rebuild: o.write("Performing full rebuild\n") print "[INFO] Performing full rebuild. This will take a little bit. Hold tight..." sys.stdout.flush() project = Projector(name,sdk_version,template_dir,project_dir,appid) project.create(template_dir,iphone_dir) force_xcode = True if os.path.exists(app_dir): shutil.rmtree(app_dir) # we have to re-copy if we have a custom version write_info_plist(infoplist_tmpl) # since compiler will generate the module dependencies, we need to # attempt to compile to get it correct for the first time. compiler = Compiler(project_dir,appid,name,deploytype,xcode_build,devicefamily,iphone_version,True) else: contents="TI_VERSION=%s\n"% sdk_version contents+="TI_SDK_DIR=%s\n" % template_dir.replace(sdk_version,'$(TI_VERSION)') contents+="TI_APPID=%s\n" % appid contents+="OTHER_LDFLAGS[sdk=iphoneos4*]=$(inherited) -weak_framework iAd\n" contents+="OTHER_LDFLAGS[sdk=iphonesimulator4*]=$(inherited) -weak_framework iAd\n" contents+="#include \"module\"\n" xcconfig = open(project_xcconfig,'w+') xccontents = xcconfig.read() if contents!=xccontents: o.write("writing contents of %s:\n\n%s\n" % (project_xcconfig,contents)) o.write("old contents\n\n%s\n" % (xccontents)) xcconfig.write(contents) xcconfig.close() else: o.write("Skipping writing contents of xcconfig %s\n" % project_xcconfig) # write out any modules into the xcode project # this must be done after project create above or this will be overriden if len(module_lib_search_path)>0: proj = PBXProj() xcode_proj = os.path.join(iphone_dir,'%s.xcodeproj'%name,'project.pbxproj') current_xcode = open(xcode_proj).read() for tp in module_lib_search_path: proj.add_static_library(tp[0],tp[1]) out = proj.parse(xcode_proj) # since xcode changes can be destructive, only write as necessary (if changed) if current_xcode!=out: xo = open(xcode_proj,'w') xo.write(out) xo.close() cwd = os.getcwd() # check to see if the symlink exists and that it points to the # right version of the library libticore = os.path.join(template_dir,'libTiCore.a') make_link = True symlink = os.path.join(iphone_dir,'lib','libTiCore.a') if os.path.islink(symlink): path = os.path.realpath(symlink) if path.find(sdk_version) > 0: make_link = False if make_link: libdir = os.path.join(iphone_dir,'lib') if not os.path.exists(libdir): os.makedirs(libdir) os.chdir(libdir) # a broken link will not return true on os.path.exists # so we need to use brute force try: os.unlink("libTiCore.a") except: pass try: os.symlink(libticore,"libTiCore.a") except: pass os.chdir(cwd) # if the lib doesn't exist, force a rebuild since it's a new build if not os.path.exists(os.path.join(iphone_dir,'lib','libtiverify.a')): shutil.copy(os.path.join(template_dir,'libtiverify.a'),os.path.join(iphone_dir,'lib','libtiverify.a')) # compile JSS files cssc = csscompiler.CSSCompiler(os.path.join(project_dir,'Resources'),devicefamily,appid) app_stylesheet = os.path.join(iphone_dir,'Resources','stylesheet.plist') asf = codecs.open(app_stylesheet,'w','utf-8') asf.write(cssc.code) asf.close() if command=='simulator': debug_sim_dir = os.path.join(iphone_dir,'build','Debug-iphonesimulator','%s.app' % name) if os.path.exists(debug_sim_dir): app_stylesheet = os.path.join(iphone_dir,'build','Debug-iphonesimulator','%s.app' % name,'stylesheet.plist') asf = codecs.open(app_stylesheet,'w','utf-8') asf.write(cssc.code) asf.close() if command!='simulator': # compile plist into binary format so it's faster to load # we can be slow on simulator os.system("/usr/bin/plutil -convert binary1 \"%s\"" % app_stylesheet) o.write("Generated the following stylecode code:\n\n") o.write(cssc.code) o.write("\n") # generate the Info.plist file with the appropriate device family if devicefamily!=None: applogo = ti.generate_infoplist(infoplist,appid,devicefamily,project_dir,iphone_version) else: applogo = ti.generate_infoplist(infoplist,appid,'iphone',project_dir,iphone_version) # copy over the appicon if applogo==None and ti.properties.has_key('icon'): applogo = ti.properties['icon'] # attempt to load any compiler plugins if len(ti.properties['plugins']) > 0: local_compiler_dir = os.path.abspath(os.path.join(project_dir,'plugins')) tp_compiler_dir = os.path.abspath(os.path.join(titanium_dir,'plugins')) if not os.path.exists(tp_compiler_dir) and not os.path.exists(local_compiler_dir): o.write("+ Missing plugins directory at %s\n" % tp_compiler_dir) print "[ERROR] Build Failed (Missing plugins directory). Please see output for more details" sys.stdout.flush() sys.exit(1) compiler_config = { 'platform':'ios', 'devicefamily':devicefamily, 'simtype':simtype, 'tiapp':ti, 'project_dir':project_dir, 'titanium_dir':titanium_dir, 'appid':appid, 'iphone_version':iphone_version, 'template_dir':template_dir, 'project_name':name, 'command':command, 'deploytype':deploytype, 'build_dir':build_dir, 'app_name':app_name, 'app_dir':app_dir, 'iphone_dir':iphone_dir } for plugin in ti.properties['plugins']: local_plugin_file = os.path.join(local_compiler_dir,plugin['name'],'plugin.py') plugin_file = os.path.join(tp_compiler_dir,plugin['name'],plugin['version'],'plugin.py') if not os.path.exists(local_plugin_file) and not os.path.exists(plugin_file): o.write("+ Missing plugin at %s (checked %s also)\n" % (plugin_file,local_plugin_file)) print "[ERROR] Build Failed (Missing plugin for %s). Please see output for more details" % plugin['name'] sys.stdout.flush() sys.exit(1) o.write("+ Detected plugin: %s/%s\n" % (plugin['name'],plugin['version'])) print "[INFO] Detected compiler plugin: %s/%s" % (plugin['name'],plugin['version']) code_path = plugin_file if os.path.exists(local_plugin_file): code_path = local_plugin_file o.write("+ Loading compiler plugin at %s\n" % code_path) compiler_config['plugin']=plugin fin = open(code_path, 'rb') m = hashlib.md5() m.update(open(code_path,'rb').read()) code_hash = m.hexdigest() p = imp.load_source(code_hash, code_path, fin) p.compile(compiler_config) fin.close() try: os.chdir(iphone_dir) # we always target backwards to 3.1 even when we use a later # version iOS SDK. this ensures our code will run on old devices # no matter which SDK we compile with deploy_target = "IPHONEOS_DEPLOYMENT_TARGET=3.1" device_target = 'TARGETED_DEVICE_FAMILY=1' # this is non-sensical, but you can't pass empty string # clean means we need to nuke the build if clean_build or force_destroy_build: print "[INFO] Performing clean build" o.write("Performing clean build...\n") if os.path.exists(app_dir): shutil.rmtree(app_dir) if not os.path.exists(app_dir): os.makedirs(app_dir) # compile localization files # Using app_name here will cause the locale to be put in the WRONG bundle!! localecompiler.LocaleCompiler(name,project_dir,devicefamily,command).compile() # copy any module resources if len(module_asset_dirs)>0: for e in module_asset_dirs: copy_module_resources(e[0],e[1],True) # copy any custom fonts in (only runs in simulator) # since we need to make them live in the bundle in simulator if len(custom_fonts)>0: for f in custom_fonts: print "[INFO] Detected custom font: %s" % os.path.basename(f) shutil.copy(f,app_dir) # dump out project file info if command!='simulator': dump_resources_listing(project_dir,o) dump_infoplist(infoplist,o) # copy Default.png and appicon each time so if they're # changed they'll stick get picked up app_icon_path = os.path.join(project_dir,'Resources','iphone',applogo) if not os.path.exists(app_icon_path): app_icon_path = os.path.join(project_dir,'Resources',applogo) if os.path.exists(app_icon_path): shutil.copy(app_icon_path,app_dir) defaultpng_path = os.path.join(project_dir,'Resources','iphone','Default.png') if not os.path.exists(defaultpng_path): defaultpng_path = os.path.join(project_dir,'Resources','Default.png') if os.path.exists(defaultpng_path): shutil.copy(defaultpng_path,app_dir) extra_args = None if devicefamily!=None: # Meet the minimum requirements for ipad when necessary if devicefamily == 'ipad' or devicefamily == 'universal': device_target="TARGETED_DEVICE_FAMILY=2" # iPad requires at a minimum 3.2 (not 3.1 default) if devicefamily == 'ipad': deploy_target = "IPHONEOS_DEPLOYMENT_TARGET=3.2" # NOTE: this is very important to run on device -- i dunno why # xcode warns that 3.2 needs only armv7, but if we don't pass in # armv6 we get crashes on device extra_args = ["VALID_ARCHS=armv6 armv7 i386"] # Additionally, if we're universal, change the device family target if devicefamily == 'universal': device_target="TARGETED_DEVICE_FAMILY=1,2" def execute_xcode(sdk,extras,print_output=True): config = name if devicefamily=='ipad': config = "%s-iPad" % config if devicefamily=='universal': config = "%s-universal" % config # these are the arguments for running a command line xcode build args = ["xcodebuild","-target",config,"-configuration",target,"-sdk",sdk] if extras!=None and len(extras)>0: args += extras args += [deploy_target,device_target] if extra_args!=None and len(extra_args)>0: args += extra_args o.write("Starting Xcode compile with the following arguments:\n\n") for arg in args: o.write(" %s\n" % arg) o.write("\napp_id = %s\n" % appid) o.write("\n\n") o.flush() if print_output: print "[DEBUG] compile checkpoint: %0.2f seconds" % (time.time()-start_time) print "[INFO] Executing XCode build..." print "[BEGIN_VERBOSE] Executing XCode Compiler <span>[toggle output]</span>" output = run.run(args,False,False,o) if print_output: print output print "[END_VERBOSE]" sys.stdout.flush() # Output already written by run.run #o.write(output) # check to make sure the user doesn't have a custom build location # configured in Xcode which currently causes issues with titanium idx = output.find("TARGET_BUILD_DIR ") if idx > 0: endidx = output.find("\n",idx) if endidx > 0: target_build_dir = dequote(output[idx+17:endidx].strip()) if target_build_dir!=build_dir: o.write("+ TARGET_BUILD_DIR = %s\n" % target_build_dir) print "[ERROR] Your TARGET_BUILD_DIR is incorrectly set. Most likely you have configured in Xcode a customized build location. Titanium does not currently support this configuration." print "[ERROR] Expected dir %s, was: %s" % (build_dir,target_build_dir) sys.stdout.flush() sys.exit(1) # look for build error if output.find("** BUILD FAILED **")!=-1 or output.find("ld returned 1")!=-1 or output.find("The following build commands failed:")!=-1: o.write("+ Detected build failure\n") print "[ERROR] Build Failed. Please see output for more details" sys.stdout.flush() sys.exit(1) o.write("+ Looking for application binary at %s\n" % binary) # make sure binary exists if not os.path.exists(binary): o.write("+ Missing application binary at %s\n" % binary) print "[ERROR] Build Failed (Missing app at %s). Please see output for more details" % binary sys.stdout.flush() sys.exit(1) # look for a code signing error error = re.findall(r'Code Sign error:(.*)',output) if len(error) > 0: o.write("+ Detected code sign error: %s\n" % error[0]) print "[ERROR] Code sign error: %s" % error[0].strip() sys.stdout.flush() sys.exit(1) # build the final release distribution args = [] if command!='simulator': # allow the project to have its own custom entitlements custom_entitlements = os.path.join(project_dir,"Entitlements.plist") entitlements_contents = None if os.path.exists(custom_entitlements): entitlements_contents = open(custom_entitlements).read() o.write("Found custom entitlements: %s\n" % custom_entitlements) else: # attempt to customize it by reading prov profile entitlements_contents = generate_customized_entitlements(provisioning_profile,appid,appuuid,command,o) o.write("Generated the following entitlements:\n\n%s\n\n" % entitlements_contents) f=open(os.path.join(iphone_resources_dir,'Entitlements.plist'),'w+') f.write(entitlements_contents) f.close() args+=["CODE_SIGN_ENTITLEMENTS = Resources/Entitlements.plist"] # only build if force rebuild (different version) or # the app hasn't yet been built initially if ti.properties['guid']!=log_id or force_xcode: log_id = ti.properties['guid'] f = open(version_file,'w+') f.write("%s,%s,%s,%s" % (template_dir,log_id,lib_hash,githash)) f.close() # this is a simulator build if command == 'simulator': if force_rebuild or force_xcode or not os.path.exists(binary): execute_xcode("iphonesimulator%s" % link_version,["GCC_PREPROCESSOR_DEFINITIONS=__LOG__ID__=%s DEPLOYTYPE=development TI_DEVELOPMENT=1 DEBUG=1 TI_VERSION=%s" % (log_id,sdk_version)],False) # first make sure it's not running kill_simulator() o.write("Finishing build\n") # sometimes the simulator doesn't remove old log files # in which case we get our logging jacked - we need to remove # them before running the simulator def cleanup_app_logfiles(): print "[DEBUG] finding old log files" sys.stdout.flush() # on OSX Snow Leopard, we can use spotlight for faster searching of log files results = run.run(['mdfind', '-onlyin', os.path.expanduser('~/Library/Application Support/iPhone Simulator/%s'%iphone_version), '-name', '%s.log'%log_id],True) if results == None: # probably not Snow Leopard def find_all_log_files(folder, fname): results = [] for root, dirs, files in os.walk(os.path.expanduser(folder)): for file in files: if fname==file: fullpath = os.path.join(root, file) results.append(fullpath) return results for f in find_all_log_files("~/Library/Application Support/iPhone Simulator/%s"%iphone_version,'%s.log' % log_id): print "[DEBUG] removing old log file: %s" % f sys.stdout.flush() os.remove(f) else: for i in results.splitlines(False): print "[DEBUG] removing old log file: %s" % i os.remove(i) cleanup_app_logfiles() sim = None # this handler will simply catch when the simulator exits # so we can exit this script def handler(signum, frame): global script_ok print "[INFO] Simulator is exiting" sys.stdout.flush() if not log == None: try: os.system("kill -2 %s" % str(log.pid)) except: pass if not sim == None and signum!=3: try: os.system("kill -3 %s" % str(sim.pid)) except: pass kill_simulator() script_ok = True sys.exit(0) # make sure we're going to stop this script whenever # the simulator exits signal.signal(signal.SIGHUP, handler) signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGQUIT, handler) signal.signal(signal.SIGABRT, handler) signal.signal(signal.SIGTERM, handler) print "[INFO] Launching application in Simulator" sys.stdout.flush() sys.stderr.flush() # launch the simulator if devicefamily==None: sim = subprocess.Popen("\"%s\" launch \"%s\" %s iphone" % (iphonesim,app_dir,iphone_version),shell=True) else: sim = subprocess.Popen("\"%s\" launch \"%s\" %s %s" % (iphonesim,app_dir,iphone_version,simtype),shell=True) # activate the simulator window - we use a OSA script to # cause the simulator window to come into the foreground (otherwise # it will be behind Titanium Developer window) ass = os.path.join(template_dir,'iphone_sim_activate.scpt') cmd = "osascript \"%s\" 2>/dev/null" % ass os.system(cmd) end_time = time.time()-start_time print "[INFO] Launched application in Simulator (%0.2f seconds)" % end_time sys.stdout.flush() sys.stderr.flush() # give the simulator a bit to get started and up and running before # starting the logger time.sleep(2) logger = os.path.realpath(os.path.join(template_dir,'logger.py')) # start the logger tail process. this will simply read the output # from the logs and stream them back to Titanium Developer on the console log = subprocess.Popen([ logger, str(log_id)+'.log', iphone_version ]) # wait (blocking this script) until the simulator exits try: os.waitpid(sim.pid,0) except SystemExit: # If the user terminates the app here, it's via a # soft kill of some kind (i.e. like what TiDev does) # and so we should suppress the usual error message. # Fixes #2086 pass print "[INFO] Application has exited from Simulator" # in this case, the user has exited the simulator itself # and not clicked Stop Emulator from within Developer so we kill # our tail log process but let simulator keep running if not log == None: try: os.system("kill -2 %s" % str(log.pid)) except: pass script_ok = True ########################################################################### # END OF SIMULATOR COMMAND ########################################################################### # # this command is run for installing an app on device # elif command == 'install': args += [ "GCC_PREPROCESSOR_DEFINITIONS=DEPLOYTYPE=test TI_TEST=1", "PROVISIONING_PROFILE[sdk=iphoneos*]=%s" % appuuid, "CODE_SIGN_IDENTITY[sdk=iphoneos*]=iPhone Developer: %s" % dist_name, "DEPLOYMENT_POSTPROCESSING=YES" ] execute_xcode("iphoneos%s" % iphone_version,args,False) print "[INFO] Installing application in iTunes ... one moment" sys.stdout.flush() if os.path.exists("/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication"): o.write("+ Preparing to run /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication\n") output = run.run(["/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication",app_dir],True) o.write("+ Finished running /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication\n") if output: o.write(output) # for install, launch itunes with the app ipa = os.path.join(os.path.dirname(app_dir),"%s.ipa" % name) o.write("+ IPA file should be at %s\n" % ipa); # it appears that sometimes this command above fails on certain installs # or is missing. let's just open if we have it otherwise, open the app # directory if not os.path.exists(ipa): # just open the app dir itself o.write("+ IPA didn't exist at %s\n" % ipa) o.write("+ Will try and open %s\n" % app_dir) ipa = app_dir # to force iTunes to install our app, we simply open the IPA # file in itunes cmd = "open -b com.apple.itunes \"%s\"" % ipa o.write("+ Executing the command: %s\n" % cmd) os.system(cmd) o.write("+ After executing the command: %s\n" % cmd) # now run our applescript to tell itunes to sync to get # the application on the phone ass = os.path.join(template_dir,'itunes_sync.scpt') cmd = "osascript \"%s\"" % ass o.write("+ Executing the command: %s\n" % cmd) os.system(cmd) o.write("+ After executing the command: %s\n" % cmd) print "[INFO] iTunes sync initiated" o.write("Finishing build\n") sys.stdout.flush() script_ok = True ########################################################################### # END OF INSTALL COMMAND ########################################################################### # # this command is run for packaging an app for distribution # elif command == 'distribute': deploytype = "production" args += [ "GCC_PREPROCESSOR_DEFINITIONS=DEPLOYTYPE=%s TI_PRODUCTION=1" % deploytype, "PROVISIONING_PROFILE[sdk=iphoneos*]=%s" % appuuid, "CODE_SIGN_IDENTITY[sdk=iphoneos*]=iPhone Distribution: %s" % dist_name, "DEPLOYMENT_POSTPROCESSING=YES" ] execute_xcode("iphoneos%s" % iphone_version,args,False) # switch to app_bundle for zip os.chdir(build_dir) # starting in 4.0, apple now requires submission through XCode # this code mimics what xcode does on its own to package the # application for the app uploader process archive_uuid = str(uuid.uuid4()).upper() archive_dir = os.path.join(os.path.expanduser("~/Library/MobileDevice/Archived Applications"),archive_uuid) archive_app_dir = os.path.join(archive_dir,"%s.app" % name) archive_appdsym_dir = os.path.join(archive_dir,"%s.app.dSYM" % name) os.makedirs(archive_app_dir) os.makedirs(archive_appdsym_dir) os.system('ditto "%s.app" "%s"' % (name,archive_app_dir)) os.system('ditto "%s.app.dSYM" "%s"' % (name,archive_appdsym_dir)) archive_plist = os.path.join(archive_dir,'ArchiveInfo.plist') o.write("Writing archive plist to: %s\n\n" % archive_plist) profile_uuid = get_profile_uuid(provisioning_profile) os.system("/usr/bin/plutil -convert xml1 -o \"%s\" \"%s\"" % (os.path.join(archive_dir,'Info.xml.plist'),os.path.join(archive_app_dir,'Info.plist'))) p = plistlib.readPlist(os.path.join(archive_dir,'Info.xml.plist')) archive_metadata = { 'CFBundleIdentifier':p['CFBundleIdentifier'], 'CFBundleVersion':p['CFBundleVersion'], 'XCApplicationFilename':'%s.app' %name, 'XCApplicationName':name, 'XCArchivedDate': time.time() - 978307200.0, 'XCArchiveUUID':archive_uuid, 'XCInfoPlist' : p, 'XCProfileUUID': profile_uuid } o.write("%s\n\n" % archive_metadata) plistlib.writePlist(archive_metadata,archive_plist) os.remove(os.path.join(archive_dir,'Info.xml.plist')) # open xcode + organizer after packaging ass = os.path.join(template_dir,'xcode_organizer.scpt') cmd = "osascript \"%s\"" % ass os.system(cmd) o.write("Finishing build\n") script_ok = True ########################################################################### # END OF DISTRIBUTE COMMAND ########################################################################### finally: os.chdir(cwd) except: print "[ERROR] Error: %s" % traceback.format_exc() if not script_ok: o.write("\nException detected in script:\n") traceback.print_exc(file=o) o.close() sys.exit(1) else: o.close()
def main(args): global script_ok argc = len(args) start_time = time.time() command = args[1].decode("utf-8") ensure_dev_path() target = 'Debug' deploytype = 'development' devicefamily = 'iphone' debug = False build_only = False simulator = False xcode_build = False force_xcode = False simtype = devicefamily # the run command is when you run from titanium using the run command # and it will run the project in the current directory immediately in the simulator # from the command line if command == 'run': project_dir = os.path.expanduser(dequote(args[2].decode("utf-8"))) iphone_version = dequote(args[3].decode("utf-8")) iphone_dir = os.path.abspath(os.path.join(project_dir,'build','iphone')) tiapp_xml = os.path.join(project_dir,'tiapp.xml') ti = TiAppXML(tiapp_xml) appid = ti.properties['id'] name = ti.properties['name'] command = 'simulator' # switch it so that the rest of the stuff works else: iphone_version = dequote(args[2].decode("utf-8")) project_dir = os.path.expanduser(dequote(args[3].decode("utf-8"))) appid = dequote(args[4].decode("utf-8")) name = dequote(args[5].decode("utf-8")) tiapp_xml = os.path.join(project_dir,'tiapp.xml') ti = TiAppXML(tiapp_xml) app_name = make_app_name(name) iphone_dir = os.path.abspath(os.path.join(project_dir,'build','iphone')) # We need to create the iphone dir if necessary, now that # the tiapp.xml allows build target selection if not os.path.isdir(iphone_dir): if os.path.exists(iphone_dir): os.remove(iphone_dir) os.makedirs(iphone_dir) project_xcconfig = os.path.join(iphone_dir,'project.xcconfig') target = 'Release' ostype = 'os' version_file = None log_id = None provisioning_profile = None debughost = None debugport = None postbuild_modules = [] # starting in 1.4, you don't need to actually keep the build/iphone directory # if we don't find it, we'll just simply re-generate it if not os.path.exists(iphone_dir): from iphone import IPhone print "[INFO] Detected missing project but that's OK. re-creating it..." iphone_creator = IPhone(name,appid) iphone_creator.create(iphone_dir,True) sys.stdout.flush() # we use different arguments dependent on the command # pluck those out here if command == 'distribute': iphone_version = check_iphone_sdk(iphone_version) link_version = iphone_version dist_keychain = None appuuid = dequote(args[6].decode("utf-8")) dist_name = dequote(args[7].decode("utf-8")) devicefamily = dequote(args[8].decode("utf-8")) print "[INFO] Switching to production mode for distribution" deploytype = 'production' elif command in ['simulator', 'build']: link_version = check_iphone_sdk(iphone_version) deploytype = 'development' debug = True simulator = command == 'simulator' build_only = command == 'build' target = 'Debug' ostype = 'simulator' devicefamily = dequote(args[4].decode("utf-8")) simtype = devicefamily elif command in ['install', 'adhoc']: iphone_version = check_iphone_sdk(iphone_version) devicefamily = dequote(args[6].decode("utf-8")) link_version = iphone_version dist_keychain = None if command == 'install': target = 'Debug' deploytype = 'test' elif command == 'adhoc': target = 'Release' deploytype = 'production' # setup up the useful directories we need in the script build_out_dir = os.path.abspath(os.path.join(iphone_dir,'build')) build_dir = os.path.abspath(os.path.join(build_out_dir,'%s-iphone%s'%(target,ostype))) app_dir = os.path.abspath(os.path.join(build_dir,name+'.app')) binary = os.path.join(app_dir,name) sdk_version = os.path.basename(os.path.abspath(os.path.join(template_dir,'../'))) iphone_resources_dir = os.path.join(iphone_dir,'Resources') version_file = os.path.join(iphone_resources_dir,'.version') force_rebuild = read_project_version(project_xcconfig)!=sdk_version or not os.path.exists(version_file) infoplist = os.path.join(iphone_dir,'Info.plist') githash = None custom_fonts = [] # if we're not running in the simulator we want to clean out the build directory if command!='simulator' and os.path.exists(build_out_dir): shutil.rmtree(build_out_dir) if not os.path.exists(build_out_dir): os.makedirs(build_out_dir) # write out the build log, useful for debugging o = codecs.open(os.path.join(build_out_dir,'build.log'),'w',encoding='utf-8') def log(msg): print msg o.write(msg) try: buildtime = datetime.datetime.now() o.write("%s\n" % ("="*80)) o.write("Appcelerator Titanium Diagnostics Build Log\n") o.write("The contents of this file are useful to send to Appcelerator Support if\n") o.write("reporting an issue to help us understand your environment, build settings\n") o.write("and aid in debugging. Please attach this log to any issue that you report.\n") o.write("%s\n\n" % ("="*80)) o.write("Starting build at %s\n\n" % buildtime.strftime("%m/%d/%y %H:%M")) # write out the build versions info versions_txt = read_config(os.path.join(template_dir,'..','version.txt')) o.write("Build details:\n\n") for key in versions_txt: o.write(" %s=%s\n" % (key,versions_txt[key])) o.write("\n\n") if versions_txt.has_key('githash'): githash = versions_txt['githash'] o.write("Script arguments:\n") for arg in args: o.write(unicode(" %s\n" % arg, 'utf-8')) o.write("\n") o.write("Building from: %s\n" % template_dir) o.write("Platform: %s\n\n" % platform.version()) # print out path to debug xcode_path=run.run(["/usr/bin/xcode-select","-print-path"],True,False) if xcode_path: o.write("Xcode path is: %s\n" % xcode_path) else: o.write("Xcode path undetermined\n") # find the module directory relative to the root of the SDK titanium_dir = os.path.abspath(os.path.join(template_dir,'..','..','..','..')) tp_module_dir = os.path.abspath(os.path.join(titanium_dir,'modules','iphone')) force_destroy_build = command!='simulator' detector = ModuleDetector(project_dir) missing_modules, modules = detector.find_app_modules(ti, 'iphone') module_lib_search_path, module_asset_dirs = locate_modules(modules, project_dir, app_dir, log) common_js_modules = [] if len(missing_modules) != 0: print '[ERROR] Could not find the following required iOS modules:' for module in missing_modules: print "[ERROR]\tid: %s\tversion: %s" % (module['id'], module['version']) exit(1) # search for modules that the project is using # and make sure we add them to the compile for module in modules: if module.js: common_js_modules.append(module) continue module_id = module.manifest.moduleid.lower() module_version = module.manifest.version module_lib_name = ('lib%s.a' % module_id).lower() # check first in the local project local_module_lib = os.path.join(project_dir, 'modules', 'iphone', module_lib_name) local = False if os.path.exists(local_module_lib): module_lib_search_path.append([module_lib_name, local_module_lib]) local = True log("[INFO] Detected third-party module: %s" % (local_module_lib)) else: if module.lib is None: module_lib_path = module.get_resource(module_lib_name) log("[ERROR] Third-party module: %s/%s missing library at %s" % (module_id, module_version, module_lib_path)) sys.exit(1) module_lib_search_path.append([module_lib_name, os.path.abspath(module.lib).rsplit('/',1)[0]]) log("[INFO] Detected third-party module: %s/%s" % (module_id, module_version)) force_xcode = True if not local: # copy module resources img_dir = module.get_resource('assets', 'images') if os.path.exists(img_dir): dest_img_dir = os.path.join(app_dir, 'modules', module_id, 'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) module_asset_dirs.append([img_dir, dest_img_dir]) # copy in any module assets module_assets_dir = module.get_resource('assets') if os.path.exists(module_assets_dir): module_dir = os.path.join(app_dir, 'modules', module_id) module_asset_dirs.append([module_assets_dir, module_dir]) full_version = sdk_version if 'version' in versions_txt: full_version = versions_txt['version'] if 'timestamp' in versions_txt or 'githash' in versions_txt: full_version += ' (' if 'timestamp' in versions_txt: full_version += '%s' % versions_txt['timestamp'] if 'githash' in versions_txt: full_version += ' %s' % versions_txt['githash'] full_version += ')' print "[INFO] Titanium SDK version: %s" % full_version print "[INFO] iPhone Device family: %s" % devicefamily print "[INFO] iPhone SDK version: %s" % iphone_version if simulator or build_only: print "[INFO] iPhone simulated device: %s" % simtype # during simulator we need to copy in standard built-in module files # since we might not run the compiler on subsequent launches for module_name in ('facebook','ui'): img_dir = os.path.join(template_dir,'modules',module_name,'images') dest_img_dir = os.path.join(app_dir,'modules',module_name,'images') if not os.path.exists(dest_img_dir): os.makedirs(dest_img_dir) module_asset_dirs.append([img_dir,dest_img_dir]) # when in simulator since we point to the resources directory, we need # to explicitly copy over any files ird = os.path.join(project_dir,'Resources','iphone') if os.path.exists(ird): module_asset_dirs.append([ird,app_dir]) # We also need to copy over the contents of 'platform/iphone' platform_iphone = os.path.join(project_dir,'platform','iphone') if os.path.exists(platform_iphone): module_asset_dirs.append([platform_iphone,app_dir]) for ext in ('ttf','otf'): for f in glob.glob('%s/*.%s' % (os.path.join(project_dir,'Resources'),ext)): custom_fonts.append(f) if (command != 'adhoc' and command != 'install'): if not (simulator or build_only): version = ti.properties['version'] # we want to make sure in debug mode the version always changes version = "%s.%d" % (version,time.time()) if (deploytype != 'production'): ti.properties['version']=version pp = os.path.expanduser("~/Library/MobileDevice/Provisioning Profiles/%s.mobileprovision" % appuuid) provisioning_profile = read_provisioning_profile(pp,o) create_info_plist(ti, template_dir, project_dir, infoplist) applogo = None clean_build = False # check to see if the appid is different (or not specified) - we need to re-generate if read_project_appid(project_xcconfig)!=appid or not infoplist_has_appid(infoplist,appid): clean_build = True force_xcode = True new_lib_hash = None lib_hash = None existing_git_hash = None # this code simply tries and detect if we're building a different # version of the project (or same version but built from different git hash) # and if so, make sure we force rebuild so to propagate any code changes in # source code (either upgrade or downgrade) if os.path.exists(app_dir): if os.path.exists(version_file): line = open(version_file).read().strip() lines = line.split(",") v = lines[0] log_id = lines[1] if len(lines) > 2: lib_hash = lines[2] existing_git_hash = lines[3] if lib_hash==None: force_rebuild = True else: if template_dir==v and force_rebuild==False: force_rebuild = False else: log_id = None else: force_rebuild = True else: force_rebuild = True o.write("\ngithash=%s, existing_git_hash=%s\n" %(githash,existing_git_hash)) if githash!=existing_git_hash: force_rebuild = True # we want to read the md5 of the libTiCore.a library since it must match # the current one we're building and if not, we need to force a rebuild since # that means we've copied in a different version of the library and we need # to rebuild clean to avoid linking errors source_lib=os.path.join(template_dir,'libTiCore.a') fd = open(source_lib,'rb') m = hashlib.md5() m.update(fd.read(1024)) # just read 1K, it's binary new_lib_hash = m.hexdigest() fd.close() if new_lib_hash!=lib_hash: force_rebuild=True o.write("forcing rebuild since libhash (%s) not matching (%s)\n" % (lib_hash,new_lib_hash)) lib_hash=new_lib_hash # when we force rebuild, we need to re-compile and re-copy source, libs etc if force_rebuild: o.write("Performing full rebuild\n") print "[INFO] Performing full rebuild. This will take a little bit. Hold tight..." sys.stdout.flush() project = Projector(name,sdk_version,template_dir,project_dir,appid) project.create(template_dir,iphone_dir) force_xcode = True if os.path.exists(app_dir): shutil.rmtree(app_dir) # we have to re-copy if we have a custom version create_info_plist(ti, template_dir, project_dir, infoplist) # since compiler will generate the module dependencies, we need to # attempt to compile to get it correct for the first time. compiler = Compiler(project_dir,appid,name,deploytype) compiler.compileProject(xcode_build,devicefamily,iphone_version,True) else: if simulator: softlink_for_simulator(project_dir,app_dir) contents="TI_VERSION=%s\n"% sdk_version contents+="TI_SDK_DIR=%s\n" % template_dir.replace(sdk_version,'$(TI_VERSION)') contents+="TI_APPID=%s\n" % appid contents+="OTHER_LDFLAGS[sdk=iphoneos*]=$(inherited) -weak_framework iAd\n" contents+="OTHER_LDFLAGS[sdk=iphonesimulator*]=$(inherited) -weak_framework iAd\n" contents+="#include \"module\"\n" xcconfig = open(project_xcconfig,'w+') xccontents = xcconfig.read() if contents!=xccontents: o.write("writing contents of %s:\n\n%s\n" % (project_xcconfig,contents)) o.write("old contents\n\n%s\n" % (xccontents)) xcconfig.write(contents) xcconfig.close() else: o.write("Skipping writing contents of xcconfig %s\n" % project_xcconfig) # write out any modules into the xcode project # this must be done after project create above or this will be overriden link_modules(module_lib_search_path, name, iphone_dir) cwd = os.getcwd() # check to see if the symlink exists and that it points to the # right version of the library libticore = os.path.join(template_dir,'libTiCore.a') make_link = True symlink = os.path.join(iphone_dir,'lib','libTiCore.a') if os.path.islink(symlink): path = os.path.realpath(symlink) if path.find(sdk_version) > 0: make_link = False if make_link: libdir = os.path.join(iphone_dir,'lib') if not os.path.exists(libdir): os.makedirs(libdir) os.chdir(libdir) # a broken link will not return true on os.path.exists # so we need to use brute force try: os.unlink("libTiCore.a") except: pass try: os.symlink(libticore,"libTiCore.a") except: pass os.chdir(cwd) # if the lib doesn't exist, force a rebuild since it's a new build if not os.path.exists(os.path.join(iphone_dir,'lib','libtiverify.a')): shutil.copy(os.path.join(template_dir,'libtiverify.a'),os.path.join(iphone_dir,'lib','libtiverify.a')) if not os.path.exists(os.path.join(iphone_dir,'lib','libti_ios_debugger.a')): shutil.copy(os.path.join(template_dir,'libti_ios_debugger.a'),os.path.join(iphone_dir,'lib','libti_ios_debugger.a')) # compile JSS files cssc = csscompiler.CSSCompiler(os.path.join(project_dir,'Resources'),devicefamily,appid) app_stylesheet = os.path.join(iphone_dir,'Resources','stylesheet.plist') asf = codecs.open(app_stylesheet,'w','utf-8') asf.write(cssc.code) asf.close() # compile debugger file debug_plist = os.path.join(iphone_dir,'Resources','debugger.plist') # Force an xcodebuild if the debugger.plist has changed force_xcode = write_debugger_plist(debughost, debugport, template_dir, debug_plist) if command not in ['simulator', 'build']: # compile plist into binary format so it's faster to load # we can be slow on simulator os.system("/usr/bin/plutil -convert binary1 \"%s\"" % app_stylesheet) o.write("Generated the following stylecode code:\n\n") o.write(cssc.code) o.write("\n") # generate the Info.plist file with the appropriate device family if devicefamily!=None: applogo = ti.generate_infoplist(infoplist,appid,devicefamily,project_dir,iphone_version) else: applogo = ti.generate_infoplist(infoplist,appid,'iphone',project_dir,iphone_version) # attempt to load any compiler plugins if len(ti.properties['plugins']) > 0: local_compiler_dir = os.path.abspath(os.path.join(project_dir,'plugins')) tp_compiler_dir = os.path.abspath(os.path.join(titanium_dir,'plugins')) if not os.path.exists(tp_compiler_dir) and not os.path.exists(local_compiler_dir): o.write("+ Missing plugins directory at %s\n" % tp_compiler_dir) print "[ERROR] Build Failed (Missing plugins directory). Please see output for more details" sys.stdout.flush() sys.exit(1) compiler_config = { 'platform':'ios', 'devicefamily':devicefamily, 'simtype':simtype, 'tiapp':ti, 'project_dir':project_dir, 'titanium_dir':titanium_dir, 'appid':appid, 'iphone_version':iphone_version, 'template_dir':template_dir, 'project_name':name, 'command':command, 'deploytype':deploytype, 'build_dir':build_dir, 'app_name':app_name, 'app_dir':app_dir, 'iphone_dir':iphone_dir } for plugin in ti.properties['plugins']: local_plugin_file = os.path.join(local_compiler_dir,plugin['name'],'plugin.py') plugin_file = os.path.join(tp_compiler_dir,plugin['name'],plugin['version'],'plugin.py') if not os.path.exists(local_plugin_file) and not os.path.exists(plugin_file): o.write("+ Missing plugin at %s (checked %s also)\n" % (plugin_file,local_plugin_file)) print "[ERROR] Build Failed (Missing plugin for %s). Please see output for more details" % plugin['name'] sys.stdout.flush() sys.exit(1) o.write("+ Detected plugin: %s/%s\n" % (plugin['name'],plugin['version'])) print "[INFO] Detected compiler plugin: %s/%s" % (plugin['name'],plugin['version']) code_path = plugin_file if os.path.exists(local_plugin_file): code_path = local_plugin_file o.write("+ Loading compiler plugin at %s\n" % code_path) compiler_config['plugin']=plugin fin = open(code_path, 'rb') m = hashlib.md5() m.update(open(code_path,'rb').read()) code_hash = m.hexdigest() p = imp.load_source(code_hash, code_path, fin) module_functions = dict(inspect.getmembers(p, inspect.isfunction)) if module_functions.has_key('postbuild'): print "[DBEUG] Plugin has postbuild" o.write("+ Plugin has postbuild") postbuild_modules.append((plugin['name'], p)) p.compile(compiler_config) fin.close() try: os.chdir(iphone_dir) # target the requested value if provided; otherwise, target minimum (4.0) # or maximum iphone_version if 'min-ios-ver' in ti.ios: min_ver = ti.ios['min-ios-ver'] if min_ver < 4.0: print "[INFO] Minimum iOS version %s is lower than 4.0: Using 4.0 as minimum" % min_ver min_ver = 4.0 elif min_ver > float(iphone_version): print "[INFO] Minimum iOS version %s is greater than %s (iphone_version): Using %s as minimum" % (min_ver, iphone_version, iphone_version) min_ver = float(iphone_version) else: min_ver = 4.0 print "[INFO] Minimum iOS version: %s" % min_ver deploy_target = "IPHONEOS_DEPLOYMENT_TARGET=%s" % min_ver device_target = 'TARGETED_DEVICE_FAMILY=1' # this is non-sensical, but you can't pass empty string # No armv6 support above 4.3 or with 6.0+ SDK if min_ver >= 4.3 or float(iphone_version) >= 6.0: valid_archs = 'armv7 i386' else: valid_archs = 'armv6 armv7 i386' # clean means we need to nuke the build if clean_build or force_destroy_build: print "[INFO] Performing clean build" o.write("Performing clean build...\n") if os.path.exists(app_dir): shutil.rmtree(app_dir) if not os.path.exists(app_dir): os.makedirs(app_dir) # compile localization files # Using app_name here will cause the locale to be put in the WRONG bundle!! localecompiler.LocaleCompiler(name,project_dir,devicefamily,deploytype).compile() # copy any module resources if len(module_asset_dirs)>0: for e in module_asset_dirs: copy_module_resources(e[0],e[1],True) # copy CommonJS modules for module in common_js_modules: #module_id = module.manifest.moduleid.lower() #module_dir = os.path.join(app_dir, 'modules', module_id) #if os.path.exists(module_dir) is False: # os.makedirs(module_dir) shutil.copy(module.js, app_dir) # copy artworks, if appropriate if command in ['adhoc', 'install', 'distribute']: artworks = ['iTunesArtwork', 'iTunesArtwork@2x'] for artwork in artworks: if os.path.exists(os.path.join(project_dir, artwork)): shutil.copy(os.path.join(project_dir, artwork), app_dir) # copy any custom fonts in (only runs in simulator) # since we need to make them live in the bundle in simulator if len(custom_fonts)>0: for f in custom_fonts: font = os.path.basename(f) app_font_path = os.path.join(app_dir, font) print "[INFO] Detected custom font: %s" % font if os.path.exists(app_font_path): os.remove(app_font_path) try: shutil.copy(f,app_dir) except shutil.Error, e: print "[WARN] Not copying %s: %s" % (font, e) # dump out project file info if command not in ['simulator', 'build']: dump_resources_listing(project_dir,o) dump_infoplist(infoplist,o) install_logo(ti, applogo, project_dir, template_dir, app_dir) install_defaults(project_dir, template_dir, iphone_resources_dir) extra_args = None recompile = copy_tiapp_properties(project_dir) # if the anything changed in the application defaults then we have to force a xcode build. if recompile == True: force_xcode = recompile if devicefamily!=None: # Meet the minimum requirements for ipad when necessary if devicefamily == 'ipad' or devicefamily == 'universal': device_target="TARGETED_DEVICE_FAMILY=2" # NOTE: this is very important to run on device -- i dunno why # xcode warns that 3.2 needs only armv7, but if we don't pass in # armv6 we get crashes on device extra_args = ["VALID_ARCHS="+valid_archs] # Additionally, if we're universal, change the device family target if devicefamily == 'universal': device_target="TARGETED_DEVICE_FAMILY=1,2" kroll_coverage = "" if ti.has_app_property("ti.ios.enablecoverage"): enable_coverage = ti.to_bool(ti.get_app_property("ti.ios.enablecoverage")) if enable_coverage: kroll_coverage = "KROLL_COVERAGE=1" def execute_xcode(sdk,extras,print_output=True): config = name if devicefamily=='ipad': config = "%s-iPad" % config if devicefamily=='universal': config = "%s-universal" % config # these are the arguments for running a command line xcode build args = ["xcodebuild","-target",config,"-configuration",target,"-sdk",sdk] if extras!=None and len(extras)>0: args += extras args += [deploy_target,device_target] if extra_args!=None and len(extra_args)>0: args += extra_args o.write("Starting Xcode compile with the following arguments:\n\n") for arg in args: o.write(" %s\n" % arg) o.write("\napp_id = %s\n" % appid) o.write("\n\n") o.flush() if print_output: print "[DEBUG] compile checkpoint: %0.2f seconds" % (time.time()-start_time) print "[INFO] Executing XCode build..." print "[BEGIN_VERBOSE] Executing XCode Compiler <span>[toggle output]</span>" # h/t cbarber for this; occasionally the PCH header info gets out of sync # with the PCH file if you do the "wrong thing" and xcode isn't # smart enough to pick up these changes (since the PCH file hasn't 'changed'). run.run(['touch', '%s_Prefix.pch' % ti.properties['name']], debug=False) print "[INFO] Siently building..." sys.stdout.flush() output = run.run(args,False,False,o) if print_output: print output print "[END_VERBOSE]" sys.stdout.flush() # Output already written by run.run #o.write(output) # check to make sure the user doesn't have a custom build location # configured in Xcode which currently causes issues with titanium idx = output.find("TARGET_BUILD_DIR ") if idx > 0: endidx = output.find("\n",idx) if endidx > 0: target_build_dir = dequote(output[idx+17:endidx].strip()) if not os.path.samefile(target_build_dir,build_dir): o.write("+ TARGET_BUILD_DIR = %s\n" % target_build_dir) print "[ERROR] Your TARGET_BUILD_DIR is incorrectly set. Most likely you have configured in Xcode a customized build location. Titanium does not currently support this configuration." print "[ERROR] Expected dir %s, was: %s" % (build_dir,target_build_dir) sys.stdout.flush() sys.exit(1) # look for build error if output.find("** BUILD FAILED **")!=-1 or output.find("ld returned 1")!=-1 or output.find("The following build commands failed:")!=-1: o.write("+ Detected build failure\n") print "[ERROR] Build Failed. Please see output for more details" sys.stdout.flush() sys.exit(1) o.write("+ Looking for application binary at %s\n" % binary) # make sure binary exists if not os.path.exists(binary): o.write("+ Missing application binary at %s\n" % binary) print "[ERROR] Build Failed (Missing app at %s). Please see output for more details" % binary sys.stdout.flush() sys.exit(1) # look for a code signing error error = re.findall(r'Code Sign error:(.*)',output) if len(error) > 0: o.write("+ Detected code sign error: %s\n" % error[0]) print "[ERROR] Code sign error: %s" % error[0].strip() sys.stdout.flush() sys.exit(1) def run_postbuild(): try: if postbuild_modules: for p in postbuild_modules: o.write("Running postbuild %s" % p[0]) print "[INFO] Running postbuild %s..." % p[0] p[1].postbuild() except Exception,e: o.write("Error in post-build: %s" % e) print "[ERROR] Error in post-build: %s" % e # build the final release distribution args = [] if command not in ['simulator', 'build']: if (command != 'adhoc' and command != 'install'): # allow the project to have its own custom entitlements custom_entitlements = os.path.join(project_dir,"Entitlements.plist") entitlements_contents = None if os.path.exists(custom_entitlements): entitlements_contents = open(custom_entitlements).read() o.write("Found custom entitlements: %s\n" % custom_entitlements) else: # attempt to customize it by reading prov profile entitlements_contents = generate_customized_entitlements(provisioning_profile,appid,appuuid,command,o) o.write("Generated the following entitlements:\n\n%s\n\n" % entitlements_contents) f=open(os.path.join(iphone_resources_dir,'Entitlements.plist'),'w+') f.write(entitlements_contents) f.close() args+=["CODE_SIGN_ENTITLEMENTS=Resources/Entitlements.plist"] # only build if force rebuild (different version) or # the app hasn't yet been built initially if ti.properties['guid']!=log_id or force_xcode: log_id = ti.properties['guid'] f = open(version_file,'w+') f.write("%s,%s,%s,%s" % (template_dir,log_id,lib_hash,githash)) f.close() # both simulator and build require an xcodebuild if command in ['simulator', 'build']: debugstr = '' if debughost: debugstr = 'DEBUGGER_ENABLED=1' if force_rebuild or force_xcode or not os.path.exists(binary): execute_xcode("iphonesimulator%s" % link_version,["GCC_PREPROCESSOR_DEFINITIONS=__LOG__ID__=%s DEPLOYTYPE=development TI_DEVELOPMENT=1 DEBUG=1 TI_VERSION=%s %s %s" % (log_id,sdk_version,debugstr,kroll_coverage)],False) run_postbuild() o.write("Finishing build\n") if command == 'simulator': # sometimes the simulator doesn't remove old log files # in which case we get our logging jacked - we need to remove # them before running the simulator cleanup_app_logfiles(ti, log_id, iphone_version) script_ok = True ########################################################################### # END OF SIMULATOR COMMAND ########################################################################### # # this command is run for installing an app on device or packaging for adhoc distribution # elif command in ['install', 'adhoc']: debugstr = '' if debughost: debugstr = 'DEBUGGER_ENABLED=1' args += ["DEPLOYMENT_POSTPROCESSING=YES"] execute_xcode("iphoneos%s" % iphone_version,args,False) script_ok = True run_postbuild() ########################################################################### # END OF INSTALL/ADHOC COMMAND ########################################################################### # # this command is run for packaging an app for distribution # elif command == 'distribute': deploytype = "production" args += [ "GCC_PREPROCESSOR_DEFINITIONS=DEPLOYTYPE=%s TI_PRODUCTION=1" % deploytype, "PROVISIONING_PROFILE=%s" % appuuid, "CODE_SIGN_IDENTITY=%s" % dist_name, "DEPLOYMENT_POSTPROCESSING=YES" ] if dist_keychain is not None: args += ["OTHER_CODE_SIGN_FLAGS=--keychain %s" % dist_keychain] execute_xcode("iphoneos%s" % iphone_version,args,False) dev_path = run.run(['xcode-select','-print-path'],True,False).rstrip() package_path = os.path.join(dev_path,'Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication') ipa = os.path.join(project_dir,"%s.ipa" % name) if os.path.exists(package_path): output = run.run([package_path,app_dir,"-o",ipa],True) print "[INFO] IPA file should be at %s" % ipa o.write("Finishing build\n") script_ok = True run_postbuild()
def build_modules_info(self, resources_dir, app_bin_dir): compiler = Compiler(self.tiapp, resources_dir, self.java, app_bin_dir, os.path.dirname(app_bin_dir)) compiler.compile(compile_bytecode=False) self.app_modules = [] template_dir = os.path.dirname(sys._getframe(0).f_code.co_filename) android_modules_dir = os.path.abspath(os.path.join(template_dir, 'modules')) modules = {} for jar in os.listdir(android_modules_dir): if not jar.endswith('.jar'): continue module_path = os.path.join(android_modules_dir, jar) module_jar = zipfile.ZipFile(module_path) module_bindings = self.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings['modules'].keys(): full_api_name = module_bindings['proxies'][module_class]['proxyAttrs']['fullAPIName'] modules[module_class] = module_bindings['modules'][module_class] modules[module_class]['fullAPIName'] = full_api_name for module in compiler.modules: bindings = [] # TODO: we should also detect module properties for method in compiler.module_methods: if method.lower().startswith(module+'.') and '.' not in method: bindings.append(method[len(module)+1:]) module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower() == module: module_class = m module_apiName = modules[m]['fullAPIName'] break if module_apiName == None: continue # module wasn't found self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': bindings }) # discover app modules detector = ModuleDetector(self.project_dir) missing, detected_modules = detector.find_app_modules(self.tiapp) for missing_module in missing: print '[WARN] Couldn\'t find app module: %s' % missing_module['name'] self.custom_modules = [] for module in detected_modules: module_jar = zipfile.ZipFile(module.path) module_bindings = self.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings['modules'].keys(): module_id = module_bindings['proxies'][module_class]['proxyAttrs']['id'] print '[DEBUG] module_id = %s' % module_id if module_id == module.manifest.moduleid: print '[DEBUG] appending module: %s' % module_class self.custom_modules.append({ 'class_name': module_class, 'manifest': module.manifest })
def build_modules_info(self, resources_dir, app_bin_dir, include_all_ti_modules=False): self.app_modules = [] (modules, external_child_modules) = bindings.get_all_module_bindings() compiler = Compiler(self.tiapp, resources_dir, self.java, app_bin_dir, None, os.path.dirname(app_bin_dir), include_all_modules=include_all_ti_modules, ti_sdk_dir=self.ti_sdk_dir) compiler.compile(compile_bytecode=False, info_message=None) for module in compiler.modules: module_bindings = [] # TODO: we should also detect module properties for method in compiler.module_methods: if method.lower().startswith(module+'.') and '.' not in method: module_bindings.append(method[len(module)+1:]) module_onAppCreate = None module_class = None module_apiName = None for m in modules.keys(): if modules[m]['fullAPIName'].lower() == module: module_class = m module_apiName = modules[m]['fullAPIName'] if 'onAppCreate' in modules[m]: module_onAppCreate = modules[m]['onAppCreate'] break if module_apiName == None: continue # module wasn't found ext_modules = [] if module_class in external_child_modules: for child_module in external_child_modules[module_class]: if child_module['fullAPIName'].lower() in compiler.modules: ext_modules.append(child_module) self.app_modules.append({ 'api_name': module_apiName, 'class_name': module_class, 'bindings': module_bindings, 'external_child_modules': ext_modules, 'on_app_create': module_onAppCreate }) # discover app modules detector = ModuleDetector(self.project_dir, self.ti_sdk_dir) missing, detected_modules = detector.find_app_modules(self.tiapp, 'android') for missing_module in missing: print '[WARN] Couldn\'t find app module: %s' % missing_module['id'] self.custom_modules = [] for module in detected_modules: if module.jar == None: continue module_jar = zipfile.ZipFile(module.jar) module_bindings = bindings.get_module_bindings(module_jar) if module_bindings is None: continue for module_class in module_bindings['modules'].keys(): module_apiName = module_bindings['modules'][module_class]['apiName'] module_proxy = module_bindings['proxies'][module_class] module_id = module_proxy['proxyAttrs']['id'] module_proxy_class_name = module_proxy['proxyClassName'] module_onAppCreate = None if 'onAppCreate' in module_proxy: module_onAppCreate = module_proxy['onAppCreate'] print '[DEBUG] module_id = %s' % module_id if module_id == module.manifest.moduleid: # make sure that the module was not built before 1.8.0.1 try: module_api_version = int(module.manifest.apiversion) if module_api_version < 2: print "[ERROR] The 'apiversion' for '%s' in the module manifest is less than version 2. The module was likely built against a Titanium SDK pre 1.8.0.1. Please use a version of the module that has 'apiversion' 2 or greater" % module_id touch_tiapp_xml(os.path.join(self.project_dir, 'tiapp.xml')) sys.exit(1) except(TypeError, ValueError): print "[ERROR] The 'apiversion' for '%s' in the module manifest is not a valid value. Please use a version of the module that has an 'apiversion' value of 2 or greater set in it's manifest file" % module_id touch_tiapp_xml(os.path.join(self.project_dir, 'tiapp.xml')) sys.exit(1) print '[DEBUG] appending module: %s' % module_class self.custom_modules.append({ 'module_id': module_id, 'module_apiName': module_apiName, 'proxy_name': module_proxy_class_name, 'class_name': module_class, 'manifest': module.manifest, 'on_app_create': module_onAppCreate })