PDA

View Full Version : SDK Very unstable and super slow



Cine
21 Dec 2011, 7:56 PM
I have tried the SDK and found it to be extremely unstable and generally not very user friendly, not to mention horribly slow.

The "create jsb" seems rather stable, however, it is lacking some crucial features/documentation:

It only works when the compile.html (with the startup) file is in the root of the build. It should be possible to hide it away inside a compile dir, so it doesnt mess up
It is impossible to control the debug/compress etc settings. E.g. how to build the -dev versions?
It is impossible to control the projectname and license text, which means that you have to edit those every time you regen your file, which in any sane build environment is every time.

The "build" is very unstable:

Half the time it will build empty files (only containing the license text) without any warning
It will gladly ignore files that are none existing without any warning
It requires a very strict input format, but gives no warnings or errors when there are problems. E.g. setting options:null will result in an empty js file. Or setting a path to "xxx" instead of "xxx/" will also result in an empty js file (which is inconsistent with paths to root which is "" rather than "/").
It leave temp files in the build dir if anything goes wrong. Those should be in the %temp% folder.

And it is extremely slow

Build of 1 small file (e.g. one of the locale files) without debug or compress will take 15 seconds. It should be near instant.
Many builds of small files will take forever. To build all of the locale files into their own compressed files take over 2 minutes.
It doesn't check for changed files, so even though the source files have not changed, the output is still generated in its extremely slow manor.

mitchellsimoens
21 Dec 2011, 8:02 PM
For performance... it uses PhantomJS which only takes like 2 seconds to startup and I have many classes being built in 5 seconds.

I will agree about the documentation. It has some CLI help but people these days don't see to like CLI things.

Stability... I have never had a problem with this. It reliably creates the JSB and builds just fine for me. But I am very explicit when using my requires/uses/etc properties throughout my apps.

Cine
21 Dec 2011, 9:07 PM
You can try to build something like this:


{"projectName":"locales","licenseText":"Copyright(c) 2006-2011 locales","builds":[
{"name":"af","target":"all.af.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-af.js"}]},
{"name":"bg","target":"all.bg.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-bg.js"}]},
{"name":"ca","target":"all.ca.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ca.js"}]},
{"name":"cs","target":"all.cs.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-cs.js"}]},
{"name":"da","target":"all.da.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-da.js"}]},
{"name":"de","target":"all.de.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-de.js"}]},
{"name":"el-gr","target":"all.el-gr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-el_GR.js"}]},
{"name":"en","target":"all.en.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-en.js"}]},
{"name":"en-gb","target":"all.en-gb.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-en_GB.js"}]},
{"name":"es","target":"all.es.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-es.js"}]},
{"name":"fa","target":"all.fa.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fa.js"}]},
{"name":"fi","target":"all.fi.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fi.js"}]},
{"name":"fr","target":"all.fr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fr.js"}]},
{"name":"fr-ca","target":"all.fr-ca.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-fr_CA.js"}]},
{"name":"el","target":"all.el.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-gr.js"}]},
{"name":"he","target":"all.he.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-he.js"}]},
{"name":"hr","target":"all.hr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-hr.js"}]},
{"name":"hu","target":"all.hu.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-hu.js"}]},
{"name":"id","target":"all.id.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-id.js"}]},
{"name":"it","target":"all.it.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-it.js"}]},
{"name":"ja","target":"all.ja.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ja.js"}]},
{"name":"ko","target":"all.ko.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ko.js"}]},
{"name":"lt","target":"all.lt.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-lt.js"}]},
{"name":"lv","target":"all.lv.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-lv.js"}]},
{"name":"mk","target":"all.mk.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-mk.js"}]},
{"name":"nl","target":"all.nl.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-nl.js"}]},
{"name":"nb-no","target":"all.nb-no.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-no_NB.js"}]},
{"name":"nn-no","target":"all.nn-no.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-no_NN.js"}]},
{"name":"pl","target":"all.pl.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pl.js"}]},
{"name":"pt","target":"all.pt.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pt.js"}]},
{"name":"pt-br","target":"all.pt-br.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pt_BR.js"}]},
{"name":"pt-pt","target":"all.pt-pt.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-pt_PT.js"}]},
{"name":"ro","target":"all.ro.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ro.js"}]},
{"name":"ru","target":"all.ru.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-ru.js"}]},
{"name":"sk","target":"all.sk.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sk.js"}]},
{"name":"sl","target":"all.sl.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sl.js"}]},
{"name":"sr","target":"all.sr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sr.js"}]},
{"name":"sv-se","target":"all.sv-se.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-sv_SE.js"}]},
{"name":"th","target":"all.th.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-th.js"}]},
{"name":"tr","target":"all.tr.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-tr.js"}]},
{"name":"vi","target":"all.vi.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-vn.js"}]},
{"name":"zh-cn","target":"all.zh-cn.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-zh_CN.js"}]},
{"name":"zh-tw","target":"all.zh-tw.min.js","options":{"debug":false},"compress":true,"files":[{"path":"Scripts/Ext/locale/","name":"ext-lang-zh_TW.js"}]}]}

On my super fast machine it takes > 90 seconds. Compare that to this tool that I build in less than an hour to do the exact same thing, but it can do it in 8 seconds:



class Combine{
public static void CombineAndOptimize(string jsbPath)
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(jsbPath));
var json = File.ReadAllText(jsbPath);
var serializer = new JavaScriptSerializer();


var obj = serializer.Deserialize<JBuilder>(json);

Stopwatch sw = Stopwatch.StartNew();

List<JavaProcess> process = new List<JavaProcess>();
foreach (var build in obj.builds)
{
var jFiles = build.files.Select(x => new FileInfo((x.path + x.name).Replace('/', '\\'))).ToList();
var p = GenerateNewFile(jFiles, new FileInfo(build.target.Replace("min", "debug")), new FileInfo(build.target), build.options.debug);
process.Add(p);
}

foreach (var javaProcess in process)
javaProcess.Finalize();

Console.WriteLine("Took {0} ms", sw.ElapsedMilliseconds);
Console.ReadKey();
}


private static JavaProcess GenerateNewFile(List<FileInfo> input, FileInfo outputfiDebug, FileInfo outputfiOpt, bool debug)
{
if (outputfiDebug.Exists)
outputfiDebug.Delete();
foreach (var fi in input)
{
string readAllText = File.ReadAllText(fi.FullName);
if (!debug)
readAllText = Regex.Replace(readAllText, "//<debug>.*?//</debug>", "",
RegexOptions.Compiled | RegexOptions.Singleline);
File.AppendAllText(outputfiDebug.FullName, readAllText, Encoding.UTF8);
}

ProcessStartInfo si =
//new ProcessStartInfo( //google compiler
// @"c:\Program Files (x86)\Java\jre6\bin\java.exe",
// "-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\compiler.jar\"" +
// " --charset=UTF-8 --compilation_level=SIMPLE_OPTIMIZATIONS" +
// " --js=\"" + outputfiDebug.FullName + "\"" +
// " --js_output_file=\"" + outputfiOpt.FullName + "tmp\"" +
// " --summary_detail_level=3 --formatting=pretty_print --jscomp_off=uselessCode"
// )
new ProcessStartInfo( //ycompress
@"c:\Program Files (x86)\Java\jre6\bin\java.exe",
"-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\ycompressor.jar\"" +
" --type js --charset UTF-8" +
" -o \"" + outputfiOpt.FullName + "tmp\"" +
" \"" + outputfiDebug.FullName + "\""
)
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var p = Process.Start(si);
StringBuilder err = new StringBuilder();
StringBuilder output = new StringBuilder();
p.OutputDataReceived += (a, b) => output.Append(b.Data);
p.ErrorDataReceived += (a, b) => err.Append(b.Data);
p.BeginOutputReadLine();
p.BeginErrorReadLine();
return new JavaProcess(p, err, output, outputfiOpt);
}


private class JavaProcess
{
private readonly Process _process;
private readonly StringBuilder _err;
private readonly StringBuilder _output;
private readonly FileInfo _outputfiOpt;

public JavaProcess(Process process, StringBuilder err, StringBuilder output, FileInfo outputfiOpt)
{
_process = process;
_err = err;
_output = output;
_outputfiOpt = outputfiOpt;
}

public void Finalize()
{
_process.WaitForExit();
Console.WriteLine(_err);
Console.WriteLine(_output);
_process.Close();
if (_outputfiOpt.Exists)
File.Delete(_outputfiOpt.FullName);
File.Move(_outputfiOpt.FullName + "tmp", _outputfiOpt.FullName);
Console.WriteLine(_outputfiOpt.FullName + " combined. Optimizing ... ");
if (_err.Length > 0)
Console.WriteLine(@"check (c:\Program Files (x86)\Java\jre6\bin\java.exe)");
}

}


internal class JBuilder
{
public string projectName;
public string licenseText;
public List<JBuild> builds;
}

internal class JBuild
{
public string name;
public string target;
public JOptions options;
public bool compress;
public List<JFile> files;
}

internal class JOptions
{
public bool debug;
}

internal class JFile
{
public string path;
public string name;
}
}


btw. That locale dir is so ****ed up, doesn't anyone care about globalization at sencha?

case "gr": return "el";
case "no-nb": return "nb-no";
case "no-nn": return "nn-no";
case "vn": return "vi";
case "ukr": return NOSUCHISO;
case "sr-rs": return NOSUCHISO;
And isnt there some way to actually keep track of translation status or tooling that the rest of use can use for our translations?

mitchellsimoens
21 Dec 2011, 9:20 PM
As soon as you come back to the professionalism world... let me know so I can continue to communicate. Rants get you no where.

Cine
21 Dec 2011, 9:26 PM
And the full source.
It is a bit specific for my needs, in that it takes the original jsb3 file from the "create jsb" and then locates translated files (in the form of originalfile.js -> originalfile.iso.js) and includes those with the sencha translations. and then builds min and debug version of all.
Also checks for altered file dates, doing the compile in milliseconds if you didnt change anything.

Sorry about my ranting, just a bit annoyed having to do it myself, when there is a nice SDK, just lacking some features and working rather slow.



using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;

namespace FixupJsBuild
{
class Program
{
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
if (args.Length != 1)
Usage();
string jsbPath = args[0];
var jsb = ReadJsb(jsbPath);
FixupJsbFile(jsb);
CombineAndOptimize(jsb);

Console.WriteLine("Took {0} ms", sw.ElapsedMilliseconds);
}

private static JBuilder ReadJsb(string jsbPath)
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(jsbPath));
var json = File.ReadAllText(jsbPath);
var serializer = new JavaScriptSerializer();
return serializer.Deserialize<JBuilder>(json);
}

private static void CombineAndOptimize(JBuilder jsb)
{

List<JavaProcess> process = (from build in jsb.builds
let jFiles = build.files.Select(x => new FileInfo((x.path + x.name).Replace('/', '\\'))).ToList()
select GenerateNewFile(jFiles, new FileInfo(build.target.Replace("min", "debug")), new FileInfo(build.target), build.options.debug))
.ToList();

foreach (var javaProcess in process)
if (javaProcess != null) javaProcess.Finalize();
}


private static DateTime GetLastModified(List<FileInfo> fileInfos)
{
DateTime lastModified = DateTime.MinValue;
foreach (var fi in fileInfos)
{
if (!fi.Exists)
{
Console.WriteLine("Input file not found : " + fi.FullName);
Usage();
}
if (fi.LastWriteTimeUtc > lastModified)
lastModified = fi.LastWriteTimeUtc;
}
return lastModified;
}

private static void Usage()
{
Console.WriteLine("Usage: jsbfile");
Environment.Exit(1);
}

private static JavaProcess GenerateNewFile(List<FileInfo> input, FileInfo outputfiDebug, FileInfo outputfiOpt, bool debug)
{
DateTime lastModified = GetLastModified(input);

if (outputfiDebug.Exists && outputfiOpt.Exists &&
outputfiDebug.LastWriteTimeUtc >= lastModified &&
outputfiOpt.LastWriteTimeUtc >= lastModified)
{
Console.WriteLine("{0} is up to date - not modified", outputfiOpt.FullName);
return null;
}
if (outputfiDebug.Exists)
outputfiDebug.Delete();
foreach (var fi in input)
{
string readAllText = File.ReadAllText(fi.FullName);
if (!debug)
readAllText = Regex.Replace(readAllText, "//<debug>.*?//</debug>", "",
RegexOptions.Compiled | RegexOptions.Singleline);
File.AppendAllText(outputfiDebug.FullName, readAllText, Encoding.UTF8);
}

ProcessStartInfo si =
//new ProcessStartInfo( //google compiler
// @"c:\Program Files (x86)\Java\jre6\bin\java.exe",
// "-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\compiler.jar\"" +
// " --charset=UTF-8 --compilation_level=SIMPLE_OPTIMIZATIONS" +
// " --js=\"" + outputfiDebug.FullName + "\"" +
// " --js_output_file=\"" + outputfiOpt.FullName + "tmp\"" +
// " --summary_detail_level=3 --formatting=pretty_print --jscomp_off=uselessCode"
// )
new ProcessStartInfo( //ycompress
@"c:\Program Files (x86)\Java\jre6\bin\java.exe",
"-jar \"" + Path.GetDirectoryName(typeof(Program).Assembly.Location) + "\\ycompressor.jar\"" +
" --type js --charset UTF-8" +
" -o \"" + outputfiOpt.FullName + "tmp\"" +
" \"" + outputfiDebug.FullName + "\""
)
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var p = Process.Start(si);
StringBuilder err = new StringBuilder();
StringBuilder output = new StringBuilder();
p.OutputDataReceived += (a, b) => output.Append(b.Data);
p.ErrorDataReceived += (a, b) => err.Append(b.Data);
p.BeginOutputReadLine();
p.BeginErrorReadLine();
return new JavaProcess(p, err, output, outputfiOpt);
}

private class JavaProcess
{
private readonly Process _process;
private readonly StringBuilder _err;
private readonly StringBuilder _output;
private readonly FileInfo _outputfiOpt;

public JavaProcess(Process process, StringBuilder err, StringBuilder output, FileInfo outputfiOpt)
{
_process = process;
_err = err;
_output = output;
_outputfiOpt = outputfiOpt;
}

public void Finalize()
{
_process.WaitForExit();
Console.WriteLine(_err);
Console.WriteLine(_output);
_process.Close();
if (_outputfiOpt.Exists)
File.Delete(_outputfiOpt.FullName);
File.Move(_outputfiOpt.FullName + "tmp", _outputfiOpt.FullName);
Console.WriteLine(_outputfiOpt.FullName + " combined. Optimizing ... ");
if (_err.Length > 0)
Console.WriteLine(@"check (c:\Program Files (x86)\Java\jre6\bin\java.exe)");
}

}

private static void FixupJsbFile(JBuilder jsb)
{
jsb.projectName = "Catglobe";
jsb.licenseText = "Copyright(c) 2006-2011 Catglobe";

//remove non existing file, it always wants to add
jsb.builds = new List<JBuild> {jsb.builds[0]};
jsb.builds[0].target = "all.min.js";
jsb.builds[0].options.debug = false;
//jsb.builds.Add(new JBuild
// {
// compress = true,
// name = "optim",
// options = new JOptions {debug = false},
// target = "all.min.js",
// files = new List<JFile>
// {
// new JFile {name = "all.debug.js", path = string.Empty}
// }
// });

var translateableFiles = jsb.builds[0].files.Where(x => !x.path.StartsWith("Scripts/Ext/src")).ToList();
var pathswithpossibletranslations = translateableFiles.Select(x => x.path).Distinct();
var translatedFiles = new Dictionary<string, List<JFile>>();

const string scriptsExtLocale = "Scripts/Ext/locale";
foreach (var extlocale in Directory.EnumerateFiles(scriptsExtLocale, "*.js"))
{
var split = extlocale.Replace(".js", "").Split('-');
var locale = FindCorrectlocale(split[split.Length - 1]);
try
{
CultureInfo.GetCultureInfo(locale);
}
catch (CultureNotFoundException)
{
continue;
}
translatedFiles[locale] = new List<JFile>
{
new JFile
{
name = Path.GetFileName(extlocale),
path = Path.GetDirectoryName(extlocale).Replace('\\', '/') + '/',
}
};
}

foreach (var pathwithpossibletranslations in pathswithpossibletranslations)
{
var filecollection = Directory.EnumerateFiles(pathwithpossibletranslations, "*.js")
.Select(x => x.ToLowerInvariant()).ToList();

//we should now have a list of files named something like hest.da.js
foreach (var file in filecollection)
{
var split = file.Split('.');
if (split.Length < 2) continue;
var locale = split[split.Length - 2];
List<JFile> d;
if (!translatedFiles.TryGetValue(locale, out d))
continue;
d.Add(new JFile
{
name = Path.GetFileName(file),
path = Path.GetDirectoryName(file).Replace('\\', '/'),
});
}
}

foreach (var translatedFile in translatedFiles)
{
string locale = translatedFile.Key;
jsb.builds.Add(new JBuild
{
compress = true,
name = locale,
target = "all." + locale + ".min.js",
options = new JOptions(),
files = translatedFile.Value
});
}

//json = serializer.Serialize(obj);
//File.WriteAllText(jsbPath, json);
}

private static string FindCorrectlocale(string locale)
{
string correctlocale = locale.Replace("_", "-").ToLowerInvariant();
switch (correctlocale)
{
case "gr": return "el";
case "no-nb": return "nb-no";
case "no-nn": return "nn-no";
case "vn": return "vi";

}
return correctlocale;
}

// ReSharper disable InconsistentNaming
// ReSharper disable NotAccessedField.Local
// ReSharper disable ClassNeverInstantiated.Local
private class JBuilder
{
public string projectName;
public string licenseText;
public List<JBuild> builds;
}

private class JBuild
{
public string name;
public string target;
public JOptions options;
public bool compress;
public List<JFile> files;
}

private class JOptions
{
public bool debug;
}

private class JFile
{
public string path;
public string name;
}
// ReSharper restore ClassNeverInstantiated.Local
// ReSharper restore NotAccessedField.Local
// ReSharper restore InconsistentNaming
}

}