UNPKG

ngui-tools

Version:

A GUI typesetting display engine and cross platform GUI application development framework based on NodeJS/OpenGL

830 lines (721 loc) 26.1 kB
/* ***** BEGIN LICENSE BLOCK ***** * Distributed under the BSD license: * * Copyright (c) 2015, xuewen.chu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of xuewen.chu nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL xuewen.chu BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ***** END LICENSE BLOCK ***** */ var util = require('ngui-stew/util'); var paths = require('./paths'); var fs = require('ngui-stew/fs'); var path = require('ngui-stew/url'); var keys = require('ngui-stew/keys'); var sys = require('os'); var { NGUIBuild } = require('./build'); var { getLocalNetworkHost } = require('ngui-stew/network_host'); var { syscall } = require('ngui-stew/syscall'); var child_process = require('child_process'); var large_file_merge = require('ngui-stew/large_file_cut').merge; var native_source = [ '.c', '.cc', '.cpp', '.cxx', '.m', '.mm', '.s', '.swift', '.java', ]; function parse_json_file(filename) { try { return JSON.parse(fs.readFileSync(filename, 'utf-8')); } catch (err) { err.message = filename + ': ' + err.message; throw err; } } function Package_get_start_argv(self) { if ( self.is_app ) { var name = self.name; var pkg_json = self.pkg_json; var start_argv = name; var inspect = '--inspect=0.0.0.0:9229 '; var start_argv_debug = 'http://' + getLocalNetworkHost()[0] + ':1026/' + name + ' --dev --ignore-local=*'; //if (self.host.m_os != 'ios') { start_argv_debug = inspect + start_argv_debug; //} if ( pkg_json.skip_install ) { if ( !pkg_json.origin || !/^https?:\/\//.test(String(pkg_json.origin)) ) { console.error( 'Application', name, 'no valid boot parameters.' ); // start_argv = JSON.stringify(String(pkg_json.origin || '')); } else { start_argv = JSON.stringify(pkg_json.origin); } } return [start_argv, start_argv_debug]; } return null; } function Package_gen_ios_gypi(self) { var is_app = self.is_app; var name = self.name; var host = self.host; var sources = self.sources; var id = self.pkg_json.id || 'com.mycompany.${PRODUCT_NAME:rfc1034identifier}'; var app_name = self.pkg_json.app_name || '${EXECUTABLE_NAME}'; var version = self.pkg_json.version; var xcode_settings = {}; if ( is_app ) { // copy platfoem file xcode_settings = { 'INFOPLIST_FILE': '<(XCODE_INFOPLIST_FILE)', //'OTHER_LDFLAGS': '-all_load', 'SKIP_INSTALL': 'NO', 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', }; var out = host.m_proj_out; var template = __dirname + '/export/'+ host.m_os +'/'; var plist = out + '/' + name + '.plist'; var storyboard = out + '/' + name + '.storyboard'; var xcassets = out + '/' + name + '.xcassets'; var main = out + '/' + name + '.mm'; var str; // .plist fs.cp_sync(template + 'main.plist', plist, { replace: false }); str = fs.readFileSync(plist).toString('utf8'); var reg0 = /(\<key\>CFBundleIdentifier\<\/key\>\n\r?\s*\<string\>)([^\<]+)(\<\/string\>)/; var reg1 = /(\<key\>CFBundleDisplayName\<\/key\>\n\r?\s*\<string\>)([^\<]+)(\<\/string\>)/; var reg2 = /(\<key\>CFBundleShortVersionString\<\/key\>\n\r?\s*\<string\>)([^\<]+)(\<\/string\>)/; str = str.replace(reg0, function(a,b,c,d) { return b + id + d }); str = str.replace(reg1, function(a,b,c,d) { return b + app_name + d }); if (version) str = str.replace(reg2, function(a,b,c,d) { return b + version + d }); str = str.replace('[Storyboard]', name); fs.writeFileSync( plist, str ); // .storyboard fs.cp_sync( template + 'main.storyboard', storyboard, { replace: false } ); // .xcassets fs.cp_sync( template + 'Images.xcassets', xcassets, { replace: false } ); self.bundle_resources.push('../Project/<(os)/' + name + '.storyboard'); self.bundle_resources.push('../Project/<(os)/' + name + '.xcassets'); if ( !fs.existsSync(main) ) { // main.mm var start_argv = Package_get_start_argv(self); str = fs.readFileSync(template + 'main.mm').toString('utf8'); str = str.replace(/ARGV_DEBUG/, start_argv[1]); str = str.replace(/ARGV_RELEASE/, start_argv[0]); fs.writeFileSync( main, str ); } sources.push('../Project/<(os)/' + name + '.plist'); sources.push('../Project/<(os)/' + name + '.storyboard'); sources.push('../Project/<(os)/' + name + '.mm'); } // create gypi json data var type = is_app ? 'executable' : self.native ? 'static_pkgrary' : 'none'; var gypi = { 'targets': [ { 'variables': is_app ? { 'XCODE_INFOPLIST_FILE': '$(SRCROOT)/Project/<(os)/' + name + '.plist' } : { }, 'target_name': name, 'product_name': is_app ? name + '-1' : name, 'type': type, 'include_dirs': self.include_dirs, 'dependencies': filter_repeat(self.dependencies, name), 'direct_dependent_settings': { 'include_dirs': is_app ? [] : self.include_dirs, }, 'sources': sources, 'mac_bundle': is_app ? 1 : 0, 'mac_bundle_resources': is_app ? self.bundle_resources : [ ], 'xcode_settings': xcode_settings, } ] }; return gypi; } function Package_gen_android_gypi(self) { var is_app = self.is_app; var name = self.name; var host = self.host; var sources = self.sources; var id = (self.pkg_json.id || 'com.mycompany.' + name).replace(/-/gm, '_'); var app_name = self.pkg_json.app_name || name; var version = self.pkg_json.version; var java_pkg = id.replace(/\./mg, '/'); var so_pkg = self.native || self.native_deps ? name : 'ngui'; if ( is_app ) { // copy platfoem file var proj_out = host.m_proj_out; var app = proj_out + '/' + name; var AndroidManifest_xml = `${app}/src/main/AndroidManifest.xml`; var strings_xml = `${app}/src/main/res/values/strings.xml`; var MainActivity_java = `${app}/src/main/java/${java_pkg}/MainActivity.java`; var build_gradle = `${app}/build.gradle`; // copy android project template fs.cp_sync(__dirname + '/export/android/proj_template', proj_out, { replace: false }); // copy android app template fs.cp_sync(__dirname + '/export/android/app_template', proj_out + '/' + name, { replace: false }); fs.mkdir_p_sync(proj_out + '/' + name + '/src/main/assets'); fs.mkdir_p_sync(proj_out + '/' + name + '/src/main/java'); var str; // MainActivity.java var start_argv = Package_get_start_argv(self); fs.cp_sync(__dirname + '/export/android/MainActivity.java', MainActivity_java, { replace: false }); str = fs.readFileSync(MainActivity_java).toString('utf8'); str = str.replace(/\{id\}/gm, id); str = str.replace(/String\s+LIBRARY\s+=\s+"[^\"]+"/, `String LIBRARY = "${so_pkg}"`); str = str.replace(/ARGV_DEBUG/, start_argv[1]); str = str.replace(/ARGV_RELEASE/, start_argv[0]); fs.writeFileSync(MainActivity_java, str); // AndroidManifest.xml str = fs.readFileSync(AndroidManifest_xml).toString('utf8'); str = str.replace(/package\=\"[^\"]+\"/mg, `package="${id}"`); str = str.replace(/android\:name\=\"android\.app\.pkg_name\"\s+android\:value\=\"[^\"]+\"/, `android:name="android.app.pkg_name" android:value="${so_pkg}"`); fs.writeFileSync(AndroidManifest_xml, str); // strings.xml str = fs.readFileSync(strings_xml).toString('utf8'); str = str.replace(/name\=\"app_name\"\>[^\<]+\</, `name="app_name">${app_name}<`); fs.writeFileSync(strings_xml, str); // build.gradle str = fs.readFileSync(build_gradle).toString('utf8'); str = str.replace(/\{id\}/, id); str = str.replace(/applicationId\s+('|")[^\'\"]+('|")/, `applicationId '${id}'`); if (version) str = str.replace(/versionName\s+('|")[^\'\"]+('|")/, `versionName '${version}'`); fs.writeFileSync(build_gradle, str); } // create gypi json data var type = 'none'; if ( is_app ) { if ( self.native || self.native_deps ) { type = 'shared_library'; if ( !self.native ) { fs.writeFileSync(host.m_output, fs.readFileSync(__dirname + '/export/')); fs.cp_sync(__dirname + '/export/empty.c', host.m_output + '/empty.c', { replace: false }); sources.push('empty.c'); } } } else if ( self.native ) { type = 'static_pkgrary'; } var gypi = { 'targets': [ { 'target_name': name, 'type': type, 'include_dirs': self.include_dirs, 'dependencies': filter_repeat(self.dependencies, name), 'direct_dependent_settings': { 'include_dirs': is_app ? [] : self.include_dirs, }, 'sources': sources, } ] }; return gypi; } var Package = util.class('Package', { host: null, name: '', pathname: '', source_path: '', pkg_json: null, is_app: false, native: false, native_deps: false, include_dirs: null, sources: null, bundle_resources: null, dependencies: null, includes: null, gypi: null, constructor: function(host, pkg_json, is_app) { this.host = host; this.pkg_json = pkg_json; this.is_app = is_app; this.name = pkg_json.name; this.include_dirs = [ ]; this.sources = [ 'public' ]; this.pathname = host.m_output + '/' + this.name + '.gypi'; }, initialize: function(native_deps, includes, dependencies, bundle_resources) { var self = this; var host = this.host; var pkg_json = self.pkg_json; var cur_pkg_source = host.m_cur_pkg_source_path; var relative = path.relative(host.m_output, cur_pkg_source); self.m_initialize = true; this.native_deps = native_deps; this.includes = includes; this.dependencies = dependencies; this.bundle_resources = bundle_resources; this.source_path = cur_pkg_source; /* * 外部native依赖会忽略资源的处理,需自行处理资源的拷贝,只做简项目文件包含 * 可以依赖一个native.gypi中的多个目标,使用数组表示,如果没有值使用依赖路径basename做为目标依赖名 */ for ( var pathname in pkg_json.native_deps ) { var target = pkg_json.native_deps[pathname]; if ( !util.isAbsolute(pathname) ) { pathname = cur_pkg_source + '/' + pathname; } this.includes.push(util.resolve(pathname, '/native.gypi')); if ( target ) { if ( Array.isArray(target) ) { this.dependencies = this.dependencies.concat(target); } else { this.dependencies.push( target ); } } else { this.dependencies.push( path.basename(pathname) ); } self.native_deps = true; } // add native and source if ( fs.existsSync(cur_pkg_source + '/native') ) { fs.ls_sync(cur_pkg_source + '/native').forEach(function(stat) { if ( stat.name[0] != '.' ) { if ( stat.isFile() ) { var extname = path.extname(stat.name).toLowerCase(); if (native_source.indexOf(extname) == -1) { // resources // 将非native源文件作为资源拷贝 // self.bundle_resources.push( relative + '/native/' + stat.name ); } else { // native source self.native = true; } } self.sources.push( relative + '/native/' + stat.name ); } }); } // add source fs.ls_sync(cur_pkg_source).forEach(function(stat) { if ( stat.name != 'native' && stat.name[0] != '.' ) { if ( stat.isFile() ) { var extname = path.extname(stat.name).toLowerCase(); if (native_source.indexOf(extname) == -1) { // 不添加任何 native source self.sources.push( relative + '/' + stat.name ); } } else { self.sources.push( relative + '/' + stat.name ); } } }); if ( !pkg_json.skip_install ) { // no skip install pkg this.bundle_resources.push('install/' + this.name); } if ( this.native ) { this.include_dirs = [ relative + '/native' ]; } }, gen: function() { var os = this.host.m_os; if ( os == 'ios' ) { this.gypi = Package_gen_ios_gypi(this); } else if ( os == 'android' ) { this.gypi = Package_gen_android_gypi(this); } else { throw new Error('Coming soon') } return this.gypi; }, }); function solve_pkg(self, pathname, is_app, ignore_depe) { var source_path = util.resolve(pathname); var name = path.basename(source_path); // ignore network pkg if ( /^https?:\/\//i.test(source_path) ) { return null; } var pkg = self.m_pkg_output[name]; if ( pkg ) { // Already complete return pkg; } var pkg_json = parse_json_file(source_path + '/package.json'); util.assert(pkg_json.name && pkg_json.name == name, 'Lib name must be consistent with the folder name, ' + name + ' != ' + pkg_json.name); pkg = new Package(self, pkg_json, is_app); self.m_pkg_output[name] = pkg; var includes = []; var dependencies = []; var bundle_resources = []; var native_deps = false; if ( !ignore_depe ) { function solve_external_depe(pathname) { pathname = util.isAbsolute(pathname) ? pathname : source_path + '/' + pathname; var pkg2 = solve_pkg(self, pathname, false, false); if ( pkg2 ) { includes.push(pkg2.pathname); dependencies.push(pkg2.name); includes = includes.concat(pkg2.includes); dependencies = dependencies.concat(pkg2.dependencies); bundle_resources = bundle_resources.concat(pkg2.bundle_resources); if ( pkg2.native || pkg2.native_deps ) { native_deps = true; } } } if (Array.isArray(pkg_json.external_deps)) { pkg_json.external_deps.forEach(solve_external_depe); } else { for (var pathname in pkg_json.external_deps) { solve_external_depe(pathname); } } } if ( dependencies.length == 0 ) { dependencies = [ '<@(ngui-lib)' ]; } if ( bundle_resources.length == 0 ) { bundle_resources = self.m_bundle_resources.concat(); } self.m_cur_pkg_name = name; self.m_cur_pkg_source_path = source_path; pkg.initialize(native_deps, includes, dependencies, bundle_resources); pkg.gen(); return pkg; } // reset app resources function add_default_dependencies(self, name, default_modules) { var pkg = self.m_pkg_output[name]; default_modules.forEach(function(item) { pkg.dependencies.push(item.name); pkg.bundle_resources.push.apply(pkg.bundle_resources, item.bundle_resources); pkg.includes.push(item.pathname); pkg.includes.push.apply(pkg.includes, item.includes) }); pkg.dependencies = filter_repeat(pkg.dependencies, name); pkg.bundle_resources = filter_repeat(pkg.bundle_resources); pkg.includes = filter_repeat(pkg.includes, pkg.pathname); pkg.gypi.targets[0].dependencies = pkg.dependencies; if ( self.m_os == 'ios' ) { pkg.gypi.targets[0].mac_bundle_resources = pkg.bundle_resources; } } function is_windows_env() { return /win/i.test(process.platform) && process.platform != 'darwin'; } function filter_repeat(array, ignore) { var r = {}; array.forEach(function(item) { if ( !ignore || ignore != item ) { r[item] = 1; } }); return Object.getOwnPropertyNames(r); } function gen_project_file(self, project_name) { var gyp_exec = __dirname + (is_windows_env() ? '/../gyp/gyp.bat' : '/../gyp/gyp'); var os = self.m_os; var source = self.m_source; var project = 'make'; var project_path; var out = self.m_output; var proj_out = self.m_proj_out; if ( os == 'ios' ) { project = 'xcode'; project_path = [ `${proj_out}/${project_name}.xcodeproj` ]; } else if ( os == 'android' ) { project = 'cmake-linux'; project_path = [ `${out}/android/${project_name}/out/Release/CMakeLists.txt`, `${out}/android/${project_name}/out/Debug/CMakeLists.txt`, ]; proj_out = path.relative(source, `${out}/android/${project_name}`); } // write _var.gypi var include_gypi = ' -Iout/_var.gypi'; var var_gyp = { variables: { OS: os, os: os, project: project } }; fs.writeFileSync(source + '/out/_var.gypi', JSON.stringify(var_gyp, null, 2)); paths.includes_gypi.forEach(function(str) { include_gypi += ' -I' + path.relative(source, str); }); var shell = `\ GYP_GENERATORS=${project} ${gyp_exec} \ -f ${project} \ --generator-output="${proj_out}" \ -Goutput_dir="${path.relative(source,out)}" \ -Gstandalone ${include_gypi} \ ${project_name}.gyp \ --depth=. \ `; var buf = child_process.execSync(shell); if ( buf.length ) { console.log(buf.toString()); } return project_path; } function export_result(self) { // write gyp var includes = []; var source = self.m_source; for ( var i in self.m_pkg_output ) { var pkg = self.m_pkg_output[i]; if ( pkg.is_app ) { includes.push.apply(includes, pkg.includes) includes.push(pkg.pathname); } fs.writeFileSync( pkg.pathname, JSON.stringify(pkg.gypi, null, 2)); } includes = filter_repeat(includes).map(function(pathname) { return path.relative(source, pathname); }); var ngui_gyp = paths.ngui_gyp; var gyp = { 'variables': { 'ngui-lib': [ ngui_gyp ? path.relative(source, ngui_gyp) + ':ngui-lib': 'ngui-lib' ], }, 'includes': includes, }; var project_name = self.m_project_name; var gyp_file = source + '/' + project_name +'.gyp'; // write gyp file fs.writeFileSync( gyp_file, JSON.stringify(gyp, null, 2) ); var out = gen_project_file(self, project_name); // gen target project try { child_process.execSync('open ' + out[0]); // open project } catch (e) { // } fs.rm_sync(gyp_file); // write gyp file } function write_cmake_depe_to_android_build_gradle(self, pkg, cmake, add) { var build_gradle = `${self.m_proj_out}/${pkg.name}/build.gradle`; var str = fs.readFileSync(build_gradle).toString('utf8'); str = str.replace(/^.*android\.externalNativeBuild\.cmake\.path\s*=\s*("|')[^"']*("|').*$/mg, ''); cmake = path.relative(`${self.m_proj_out}/${pkg.name}`, cmake); cmake = `android.externalNativeBuild.cmake.path = '${cmake}'`; if ( add ) { str += cmake; } fs.writeFileSync(build_gradle, str); } function export_result_android(self) { // write gyp var output = self.m_output; var proj_out = self.m_proj_out; var settings_gradle = []; var use_ndk = false; var source = self.m_source; var str; for ( var i in self.m_pkg_output ) { var pkg = self.m_pkg_output[i]; if ( pkg.is_app ) { if ( pkg.native || pkg.native_deps ) { use_ndk = true; } settings_gradle.push("':" + i + "'"); } fs.writeFileSync( pkg.pathname, JSON.stringify(pkg.gypi, null, 2)); } // gen gyp and native cmake // android 每个项目需单独创建`gyp`并生成`.cmake` for ( var i in self.m_pkg_output ) { var pkg = self.m_pkg_output[i]; if ( pkg.is_app ) { if ( pkg.native || pkg.native_deps ) { // android并不完全依赖`gyp`,只需针对native项目生成.cmake文件 var includes = pkg.includes.concat(pkg.pathname).map(function(pathname) { return path.relative(source, pathname); }); var ngui_gyp = paths.ngui_gyp; var gyp = { 'variables': { 'ngui-lib': [ ngui_gyp ? path.relative(source, ngui_gyp) + ':ngui-lib': 'ngui-lib' ], }, 'includes': includes, }; var gyp_file = source + '/' + pkg.name +'.gyp'; // write gyp file fs.writeFileSync( gyp_file, JSON.stringify(gyp, null, 2) ); // gen cmake var out = gen_project_file(self, pkg.name); // gen target project fs.rm_sync(gyp_file); // write gyp file //对于android这两个属性会影响输出库.so的默认路径,导致无法捆绑.so库文件,所以从文件中删除它 //set_target_properties(examples PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${builddir}/pkg.${TOOLSET}") //set_source_files_properties(${builddir}/pkg.${TOOLSET}/pkgexamples.so PROPERTIES GENERATED "TRUE") var reg0 = /^set_target_properties\([^ ]+ PROPERTIES LIBRARY_OUTPUT_DIRECTORY [^\)]+\)/mg; var reg1 = /^set_source_files_properties\([^ ]+ PROPERTIES GENERATED "TRUE"\)/mg; out.forEach(function(cmake) { str = fs.readFileSync(cmake).toString('utf8'); str = str.replace(reg0, '').replace(reg1, ''); fs.writeFileSync(cmake, str); }); // write CMakeLists.txt path write_cmake_depe_to_android_build_gradle(self, pkg, out[0], true); } else { write_cmake_depe_to_android_build_gradle(self, pkg, '', false); } // copy pkgrary bundle resources to android assets directory var android_assets = `${proj_out}/${pkg.name}/src/main/assets`; pkg.bundle_resources.forEach(function(res) { var basename = path.basename(res); var source = path.relative(android_assets, output + '/' + res); var target = `${android_assets}/${basename}`; if ( fs.existsSync(target) ) { fs.unlinkSync(target); } fs.symlinkSync(source, target); }); } } // write settings.gradle fs.writeFileSync(proj_out + '/settings.gradle', 'include ' + settings_gradle.join(',')); // set useDeprecatedNdk from gradle.properties str = fs.readFileSync(proj_out + '/gradle.properties').toString('utf8'); str = str.replace(/useDeprecatedNdk\s*=\s*(false|true)/, function(){ return `useDeprecatedNdk=${use_ndk}` }); fs.writeFileSync(proj_out + '/gradle.properties', str); try { child_process.execSync('open Project/android'); // open project } catch (e) { // } } /** * @class NGUIExport */ var NGUIExport = util.class('NGUIExport', { m_source: '', m_output: '', m_proj_out: '', m_os: '', m_pkg_output: null, m_cur_pkg_name : '', m_cur_pkg_source_path: '', m_default_includes: null, m_project_name: 'app', m_bundle_resources: null, constructor: function (source, os) { var self = this; this.m_source = util.resolve(source); this.m_output = util.resolve(source, 'out'); this.m_os = os; this.m_proj_out = util.resolve(source, 'Project', os); this.m_pkg_output = { }; this.m_default_includes = { }; function copy(source) { var target = self.m_output + '/libs/' + path.basename(source); fs.cp_sync(source, target, { replace: false }); return path.relative(self.m_output, target); } // copy bundle resources and includes and librarys this.m_bundle_resources = paths.bundle_resources.map(copy); paths.includes.map(copy); var librarys = paths.librarys[os] || []; if ( os == 'ios' ) { librarys.map(function(source) { var basename = path.basename(source); var target = self.m_output + '/libs/' + basename; if ( basename == 'ios' ) { // merge Framework fs.cp_sync(source, target, { replace: false, check: function(path) { return path.indexOf('ngui.framework/ngui.') == -1; }, }); // merge ngui.framework/ngui [ 'iphonesimulator/Release', /*'iphonesimulator/Debug',*/ 'iphoneos/Release', 'iphoneos/Debug' ].forEach(function(sdk) { var pathname = `${target}/${sdk}/Frameworks/ngui.framework/ngui`; if (!fs.existsSync(pathname)) { // 目标不存在进行合并 large_file_merge(`${source}/${sdk}/Frameworks/ngui.framework/ngui`, { target: pathname }); } }); var iphonesimulatorRelease = target + '/iphonesimulator/Release/Frameworks/ngui.framework'; var iphonesimulatorDebug = target + '/iphonesimulator/Debug/Frameworks/ngui.framework'; fs.mkdir_p_sync(target + '/iphonesimulator/Debug/Frameworks'); fs.rm_r_sync(iphonesimulatorDebug); fs.symlinkSync(iphonesimulatorRelease, iphonesimulatorDebug); } else { fs.cp_sync(source, target, { replace: false }); } }); } else { librarys.map(copy); } var app_keys = this.m_source + '/app.keys'; util.assert(fs.existsSync(app_keys), 'Export source does not exist ,{0}', app_keys); fs.mkdir_p_sync(this.m_output); fs.mkdir_p_sync(this.m_output + '/public'); fs.mkdir_p_sync(this.m_proj_out); }, export: function() { var self = this; var os = this.m_os; util.assert( os == 'android' || os == 'ios', 'Do not support {0} os', os); // export pkgs var default_modules = []; var pkgs_path = self.m_source + '/node_modules'; if ( fs.existsSync(pkgs_path) && fs.statSync(pkgs_path).isDirectory() ) { fs.ls_sync(pkgs_path).forEach(function(stat) { var source = pkgs_path + '/' + stat.name; if ( stat.isDirectory() && fs.existsSync(source + '/package.json') ) { default_modules.push(solve_pkg(self, source, false, true)); } }); } // export apps var app_keys = keys.parseFile(this.m_source + '/app.keys'); for ( var name in app_keys ) { if (name[0] == '@') { // 忽略 @ if ( name == '@project_name' ) { this.m_project_name = app_keys[name]; } } else { if ( ! fs.existsSync(this.m_output + '/install/' + name) ) { new NGUIBuild(this.m_source, this.m_output).build(); } util.assert(fs.existsSync(this.m_output + '/install/' + name), 'Installation directory not found'); solve_pkg(this, this.m_source + '/' + name, true, false); add_default_dependencies(this, name, default_modules); } } if ( os == 'android' ) { export_result_android(this); } else { export_result(this); } } }); exports.NGUIExport = NGUIExport;