]> git.proxmox.com Git - sencha-touch.git/blame - src/examples/map/.sencha/app/app-build.js
import Sencha Touch 2.4.2 source
[sencha-touch.git] / src / examples / map / .sencha / app / app-build.js
CommitLineData
c4685c84
TL
1importPackage(com.sencha.tools.compressors.yui);
2importPackage(com.sencha.tools.compressors.closure);
3importPackage(com.sencha.tools.external);
4importPackage(com.sencha.tools.compiler.jsb.statements);
5
6var _logger = SenchaLogManager.getLogger("app-build");
7
8function runAppBuild(proj) {
9 var basedir = proj.getProperty("framework.config.dir"),
10 appPath = proj.getProperty("args.path"),
11 envArg = proj.getProperty("args.environment"),
12 ignores = proj.getProperty("framework.ignores"),
13 options = proj.getProperty("build.options"),
14 cssCompression = proj.getProperty("build.compress.css"),
15 config = readConfig(resolvePath(appPath, "app.json")),
16 environment = (envArg == "native") ? 'package' : envArg,
17 destination =
18 (proj.getProperty("args.destination") + '') ||
19 (proj.getProperty("build.dir") + '') ||
20 (proj.getProperty("app.build.dir") + ''),
21 operations = toJS(proj.getProperty("build.operations")),
22 v2deps = !!(proj.getProperty("v2deps") == "true"),
23 src = appPath,
24 sdk = proj.getProperty("framework.dir"),
25 archive =
26 (proj.getProperty("args.archive") + '') ||
27 (config.archivePath) ||
28 "archive",
29 nativePackaging = !!(envArg == 'native'),
30 indexHtmlPath = config.indexHtmlPath || 'index.html',
31 appUrl = config.url || resolvePath(src, indexHtmlPath),
32 jsAssets = config.js || [],
33 cssAssets = config.css || [],
34 appCache = config.appCache,
35 ignore = config.ignore,
36 remoteAssets = [],
37 extras = config.extras || config.resources,
38 appJs, sdkJs, sdkIsAll, assets, processIndex;
39
40 destination = joinPath(destination, environment);
41
42 if(!PathUtil.isAbsolute(archive)) {
43 archive = resolvePath(appPath, archive);
44 }
45
46 if (operations) {
47 operations = operations.split('\n');
48 } else {
49 operations = [];
50 }
51
52 if (appUrl.indexOf("file:") != 0 && appUrl.indexOf("http:") != 0) {
53 appUrl = 'file:///' + StringUtil.replace(resolvePath(appUrl), '\\', '/');
54 }
55
56 // check for build dir being immediate child of application dir
57 // native packager can get in to infinite looping when scanning files
58 // under this scenario
59 var canonicalAppPath = new File(appPath).getCanonicalPath(),
60 canonicalDestPath = new File(destination).getCanonicalPath(),
61 parent = new File(canonicalDestPath).getParentFile();
62
63 if(parent && parent.getCanonicalPath() == canonicalAppPath) {
64 _logger.error("Application : {}", canonicalAppPath);
65 _logger.error("Destination : {}", canonicalDestPath);
66 _logger.error("Destination path cannot reside one level under the Application directory")
67 throw "Destination path cannot reside one level under the Application directory";
68 }
69
70
71 _logger.info("Deploying your application to " + destination);
72
73 PathUtil.ensurePathExists(resolvePath(destination));
74
75 jsAssets = each(
76 map(jsAssets, function (asset) {
77 if (typeof asset == 'string') {
78 asset = { path:asset };
79 }
80 asset.type = 'js';
81 return asset;
82 }),
83 function (jsAsset) {
84 if (jsAsset.bundle) {
85 appJs = jsAsset.path;
86 }
87 });
88
89 if (!appJs) {
90 appJs = 'app.js';
91 }
92
93 appJs = resolvePath(destination, appJs);
94
95 cssAssets = map(cssAssets, function (asset) {
96 if (typeof asset == 'string') {
97 asset = { path:asset };
98 }
99 asset.type = 'css';
100 return asset;
101 });
102
103 assets = filter(concat(jsAssets, cssAssets), function (asset) {
104 return !asset.shared || (environment != 'production');
105 });
106
107 _logger.debug("copying all assets");
108 each(assets, function (asset) {
109 if (asset.remote) {
110 asset.bundle = false;
111 asset.update = false;
112 remoteAssets.push(asset);
113 } else {
114 file = asset.path;
115
116 // if not in testing mode, and using the new compiler, and this is
117 // one of the sencha-touch files, don't copy to output directory
118 if( asset.type === 'js' &&
119 !v2deps &&
120 file.indexOf("sencha-touch") != -1) {
121 asset['x-bootstrap'] = true;
122
123 // only skip the sdk code in the bundle if the bundle flag
124 // on the sdk asset is explicitly set to false
125 if(('bundle' in asset) && asset.bundle === false) {
126 sdkJs = asset.path;
127 sdkIsAll = sdkJs.indexOf("-all.js") >= 0;
128 asset.isSdk = true;
129 }
130 }
131
132 if (asset['x-bootstrap'] && !asset.isSdk) {
133 return;
134 }
135
136 _logger.debug("copying file {}", resolvePath(src, file));
137
138 var srcPath = resolvePath(src, file),
139 dstPath = resolvePath(destination, stripSpecialDirNames(file));
140
141 if(srcPath != dstPath) {
142 PathUtil.ensurePathExists(dstPath);
143 copy(srcPath, dstPath);
144 _logger.info("Copied {} to {}", srcPath, dstPath);
145 }
146 }
147 });
148
149 var ignoreFilter = Filter.getFileNameFilter(
150 new RegexFilter(ignore).setInclusive(false));
151
152 _logger.debug("copying all extras");
153 each(extras, function (extra) {
154 var from = resolvePath(src, extra),
155 to = resolvePath(destination, extra);
156 _logger.debug("Copying from {} to {}", from, to);
157 if (new File(from).exists()) {
158 PathUtil.ensurePathExists(to);
159 copy(from, to, ignoreFilter);
160 _logger.info("Copied {}", from);
161 } else {
162 _logger.warn("File or folder {} not found", from);
163 }
164 });
165
166 // build the app
167
168 processIndex = function () {
169 _logger.debug("processing page : index.html");
170 jsAssets = filter(jsAssets, function(asset){
171 return !(asset['x-bootstrap'] && !asset.isSdk);
172 });
173
174 var appJson = jsonEncode({
175 id:config.id,
176 js:jsAssets,
177 css:cssAssets
178 }),
179 indexHtml, content, compressor, remotes, microloader;
180
181 writeFileContent(new File(destination, 'app.json'), appJson);
182 _logger.info("Generated app.json");
183
184 indexHtml = readFileContent(new File(src, indexHtmlPath));
185
186 if (environment == 'production' && appCache) {
187 indexHtml = StringUtil.replace(
188 indexHtml,
189 '<html manifest=""',
190 '<html manifest="cache.appcache"');
191 }
192
193 compressor = new ClosureCompressor();
194 microloader = (environment == 'production'
195 ? 'production'
196 : 'testing') +
197 '.js';
198 _logger.debug("using microloader : {}", microloader);
199 content = readFileContent(joinPath(sdk, "microloader", microloader));
200 content = compressor.compress(content);
201 remotes = [
202 '<script type="text/javascript">' +
203 content + ';Ext.blink(' +
204 (environment == 'production' ? jsonEncode({
205 id:config.id
206 }) : appJson) + ')' +
207 '</script>'
208 ];
209
210 each(remoteAssets, function (asset) {
211 var uri = asset.path;
212
213 if (asset.type === 'js') {
214 remotes.push(
215 '<script type="text/javascript" src="' +
216 uri +
217 '"></script>');
218 } else if (asset.type === 'css') {
219 remotes.push(
220 '<link rel="stylesheet" type="text/css" href="' +
221 uri +
222 '" />');
223 }
224 });
225
226 indexHtml = ('' + indexHtml).replace(
227 /<script id="microloader"([^<]+)<\/script>/,
228 remotes.join(''));
229
230 _logger.debug("generating new built index.html");
231 writeFileContent(resolvePath(destination, indexHtmlPath), indexHtml);
232 _logger.info("Embedded microloader into " + indexHtmlPath);
233 };
234
235 _logger.info("Resolving your application dependencies (" + appUrl + ")");
236
237 var preprocessor = new Parser(),
238 jsCompressor = new YuiJavascriptCompressor(),
239 cssCompressor = new YuiCssCompressor(),
240 phantomRunner = new PhantomJsRunner(),
241 processedAssetCount = 0,
242 assetsCount, dependencies, files, file,
243 destinationFile, compressor,
244 cleanFile, cleanDestinationFile;
245
246 if(v2deps) {
247 // if v2deps, use the sencha command 2 sytle dependency resolution mechanism
248 // by running the phantomjs dependencies.js script
249 var phantomOut = phantomRunner.run([
250 resolvePath(basedir, "dependencies.js"),
251 appUrl
252 ]),
253 exitCode = phantomOut.getExitCode(),
254 stdout = phantomOut.getOutput(),
255 buffer = new StringBuilder();
256
257
258 if (exitCode > 0) {
259 _logger.error("dependencies.js exited with non-zero code : " + exitCode);
260 _logger.error(stdout);
261 throw new ExBuild("failed gathering dependencies").raise();
262 }
263 dependencies = jsonDecode(stdout);
264
265 _logger.info("Found " + dependencies.length + " dependencies. Concatenating all into '" + appJs + "'");
266
267 files = map(dependencies, function (dependency) {
268 return resolvePath(src, dependency.path);
269 });
270
271 files.push(appJs);
272
273 each(files, function (file) {
274 buffer.append(FileUtil.readUnicodeFile(resolvePath(file))).append('\n');
275 });
276
277 writeFileContent(appJs, buffer.toString());
278
279 // clear the buffer to free memory
280 buffer.setLength(0);
281 } else {
282 var sdkTag = sdkIsAll ? 'framework' : 'core',
283 sdkFile = sdkJs,
284 sdkJsArgs = [
285 '--options=' + options,
286 'union',
287 '-tag',
288 sdkTag,
289 'and',
290 'concat',
291 '-out=' + resolvePath(destination, sdkFile)
292 ],
293 appJsArgs = [
294 '-options=' + options,
295 'restore',
296 'app-all',
297 'and',
298 'exclude',
299 '-tag',
300 sdkTag,
301 'and',
302 'concatenate',
303 '-out=' + appJs,
304 ''],
305 compilerId = proj.getProperty("compiler.ref.id"),
306 compiler = proj.getReference(compilerId);
307
308 if(sdkJs) {
309 _logger.info("Compiling " + sdkFile + " and dependencies");
310 _logger.debug("running compiler with options : '{}'", sdkJsArgs.join(' '));
311 compiler.dispatchArgs(sdkJsArgs);
312 _logger.info("Compiling app.js and dependencies");
313 _logger.debug("running compiler with options : '{}'", appJsArgs.join(' '));
314 compiler.dispatchArgs(appJsArgs);
315 _logger.info("Completed compilation.");
316 } else {
317 appJsArgs = [
318 '-options=' + options,
319 'restore',
320 'app-all',
321 'and',
322 'concatenate',
323 '-out=' + appJs,
324 ''];
325
326 _logger.debug("running compiler with options : '{}'", appJsArgs.join(' '));
327 compiler.dispatchArgs(appJsArgs);
328 _logger.info("Completed compilation.");
329 }
330 }
331
332
333 for (var name in config.buildOptions) {
334 if (config.buildOptions.hasOwnProperty(name)) {
335 preprocessor.setParam(name, config.buildOptions[name]);
336 }
337 }
338
339 assetsCount = assets.length;
340
341 each(assets, function (asset) {
342 if(!asset.remote) {
343 file = asset.path;
344 destinationFile = resolvePath(destination, file),
345 cleanFile = stripSpecialDirNames(file),
346 cleanDestinationFile = resolvePath(destination, cleanFile);
347
348 // adjust the asset path to use the cleaned filename
349 asset.path = cleanFile;
350 }
351
352
353 _logger.debug("Assets => Processed : {} Total : {}",
354 processedAssetCount, assetsCount);
355
356 if (asset.type == 'js') {
357 if (!asset.remote && !(asset['x-bootstrap'] && !asset.isSdk)) {
358 _logger.debug("running preprocessor for file {}", cleanDestinationFile);
359 writeFileContent(
360 cleanDestinationFile,
361 preprocessor.parse(readFileContent(cleanDestinationFile)));
362 _logger.info('Processed local file ' + asset.path);
363 } else {
364 _logger.info('Processed remote file ' + asset.path);
365 }
366 }
367
368 if (environment == 'testing') {
369 return;
370 }
371
372 if (asset.remote || (asset['x-bootstrap'] && !asset.isSdk)) {
373 ++processedAssetCount;
374 } else {
375 _logger.debug("Minifying " + file);
376
377 if(asset.type == 'js') {
378 writeFileContent(
379 cleanDestinationFile,
380 jsCompressor.compress(readFileContent(cleanDestinationFile)));
381
382 _logger.info("Minified " + file);
383 } else if (cssCompression == "true") {
384 writeFileContent(
385 cleanDestinationFile,
386 cssCompressor.compress(readFileContent(cleanDestinationFile)));
387
388 _logger.info("Minified " + file);
389 }
390
391 if (environment == 'production') {
392 var content = readFileContent(cleanDestinationFile),
393 version = '' + FileUtil.createChecksum(content);
394 asset.version = version;
395
396 _logger.debug("prepending checksum on {}", cleanDestinationFile);
397 writeFileContent(
398 cleanDestinationFile,
399 "/*" + version + "*/" + content);
400 content = "";
401
402 _logger.debug("copying destination to archive");
403
404 PathUtil.ensurePathExists(resolvePath(archive, cleanFile, version));
405 copy(cleanDestinationFile, resolvePath(archive, cleanFile, version));
406
407 if (asset.update == 'delta') {
408 // generate all the deltas to the other archived versions
409 _logger.debug("generating file deltas");
410 var archivedVersions = new File(joinPath(archive, cleanFile))
411 .listFiles();
412
413 each(archivedVersions, function (archivedVersion) {
414 if(archivedVersion.isDirectory()) {
415 return;
416 }
417
418 archivedVersion = archivedVersion.name;
419
420 if (archivedVersion == version) {
421 return;
422 }
423
424 var deltaFile = joinPath(
425 destination,
426 'deltas',
427 cleanFile,
428 archivedVersion + '.json');
429
430 writeFileContent(deltaFile, '');
431
432 _logger.debug("Generating delta from {} to {}",
433 archivedVersion,
434 version);
435
436 var runner = new VcDiffRunner(),
437 args = [
438 'encode',
439 '-json',
440 '-dictionary',
441 joinPath(archive, cleanFile, archivedVersion),
442 '-target',
443 cleanDestinationFile,
444 '-delta',
445 resolvePath(deltaFile),
446 '--stats'
447 ],
448 runnerOut = runner.run(args),
449 exitCode = runnerOut.getExitCode(),
450 stdout = runnerOut.getOutput();
451
452 if (exitCode > 0) {
453 _logger.error("failed generating diff from {} to {}",
454 archivedVersion,
455 version);
456 _logger.error(stdout);
457 throw new ExBuild("failed generating diff from {0} to {1}",
458 archivedVersion,
459 version).raise();
460 }
461
462 // fixup malformed vcdiff content
463 var deltaFilePath = resolvePath(deltaFile),
464 content = FileUtil.readFile(deltaFilePath);
465 if(content.endsWith(",]")) {
466 _logger.debug("Correcting trailing comma issue in vcdiff output");
467 FileUtil.writeFile(deltaFilePath, content.substring(0, content.length() - 2) + "]");
468 }
469
470 content = null;
471
472 _logger.info(
473 "Generated delta for: {} from hash: '{}' to hash: '{}'",
474 [cleanFile, archivedVersion, version]);
475 });
476
477 }
478 }
479
480 if (++processedAssetCount == assetsCount) {
481 _logger.debug("processed all assets, finalizing build...");
482 processIndex();
483
484 if (environment == 'production' && appCache) {
485 _logger.info("Generating appcache");
486 appCache.cache = map(appCache.cache, function (cache) {
487 var checksum = '';
488
489 if (!/^(\/|(.*):\/\/)/.test(cache)) {
490 _logger.info(
491 "Generating checksum for appCache item: {}",
492 cache);
493
494 checksum = FileUtil.createChecksum(
495 readFileData(joinPath(destination, cache)));
496 }
497
498 return {
499 uri:cache,
500 checksum:checksum
501 }
502 });
503
504 writeAppCache(appCache, joinPath(destination, 'cache.appcache'));
505 }
506
507 if (nativePackaging) {
508 _logger.info("Generating native package");
509 var packagerConfig = readConfig(
510 joinPath(src, 'packager.json'));
511
512 if (packagerConfig.platform.match(/iOS/)) {
513 copy(
514 resolvePath(joinPath(src, 'resources', 'icons')),
515 resolvePath(destination),
516 ignoreFilter);
517 copy(
518 resolvePath(joinPath(src, 'resources', 'loading')),
519 resolvePath(destination),
520 ignoreFilter);
521 }
522
523 // add '' here to coerce to javascript string instead of java string
524 // for json encoding later...
525 packagerConfig.inputPath = destination + '';
526
527 var origDestination = proj.getProperty("args.destination"),
528 nativePackagePath = proj.getProperty("native.build.dir") ||
529 joinPath(origDestination, "native");
530
531 packagerConfig.outputPath = resolvePath(nativePackagePath) + '';
532
533 PathUtil.ensurePathExists(packagerConfig.outputPath);
534
535 writeFileContent(
536 joinPath(src, 'packager.temp.json'),
537 jsonEncode(packagerConfig, true));
538
539 _logger.info(
540 "Packaging your application as a native app to {} ...",
541 packagerConfig.outputPath);
542
543 var stbuildRunner = new StBuildRunner(),
544 args = ['package', resolvePath(src, 'packager.temp.json')],
545 stbuildOut = stbuildRunner.run(args),
546 exitCode = stbuildOut.getExitCode(),
547 stdout = stbuildOut.getOutput();
548
549 if (exitCode > 0) {
550 _logger.error("failed running native packager");
551 _logger.error(stdout);
552 throw new ExBuild("failed running native packager")
553 .raise();
554 } else {
555 _logger.info("Successfully packaged native application");
556 _logger.info(
557 "Package may be run with 'sencha package run -p {}",
558 joinPath(src, 'packager.temp.json'))
559 }
560 } else {
561 _logger.debug("skipping native packaging");
562 }
563 }
564 }
565 });
566
567 if (environment == 'testing') {
568 processIndex();
569 }
570
571 _logger.info("Successfully deployed your application to " + destination);
572
573};
574
575function writeAppCache(appCache, outfile) {
576 _logger.debug("generating appcache manifest...");
577 // build the appCache file
578 var builder = new StringBuilder();
579
580 builder.append("CACHE MANIFEST\n");
581 each(appCache.cache, function (cache) {
582 builder.append("# " + cache.checksum + "\n");
583 builder.append(cache.uri + "\n");
584 });
585
586 builder.append("\n\nFALLBACK:\n");
587
588 each(appCache.fallback, function (fallback) {
589 builder.append(fallback + '\n');
590 });
591
592 builder.append("\n\nNETWORK:\n");
593
594 each(appCache.network, function (network) {
595 builder.append(network + '\n');
596 });
597
598 writeFileContent(
599 outfile,
600 builder.toString());
601
602 builder.setLength(0);
603};
604
605
606(function (proj) {
607 _logger.info("building application");
608 runAppBuild(proj);
609})(project);