[Laszlo-dev] [BULK] Re: [Laszlo-checkins] r9896 - in openlaszlo/trunk: WEB-INF/lps/server/src/org/openlaszlo/sc installer/macosx
P T Withington
ptw at pobox.com
Mon Jun 23 12:34:44 PDT 2008
Surely there is some script you will need to change in Unix and
Windows to set FLEX_HOME?
Is there any way you could us WEB-INF/lps/server/bin/lzenv to do this?
On 2008-06-23, at 15:14 EDT, hqm at openlaszlo.org wrote:
> Author: hqm
> Date: 2008-06-23 12:14:19 -0700 (Mon, 23 Jun 2008)
> New Revision: 9896
>
> Modified:
> openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/
> SWF9External.java
> openlaszlo/trunk/installer/macosx/OpenLaszlo Explorer.command.proto
> openlaszlo/trunk/installer/macosx/Start OpenLaszlo
> Server.command.proto
> openlaszlo/trunk/installer/macosx/mkpkg.sh
> Log:
> Change 20080623-hqm-b by hqm at badtzmaru.home on 2008-06-23 15:10:39 EDT
> in /Users/hqm/openlaszlo/trunk4
> for http://svn.openlaszlo.org/openlaszlo/trunk
>
> Summary: go back to 'exec' method of calling flex compiler
>
> New Features:
>
> Bugs Fixed:
>
> Technical Reviewer: ptw
> QA Reviewer: mamye
> Doc Reviewer: (pending)
>
> Documentation:
>
> Release Notes:
>
> Details:
>
> Back out of the change to use the flex "oem" compiler API, and call by
> exec'ing flex SDK scripts. This works around the bug where OSX
> installations with spaces in their pathnames caused swf9 compiles to
> fail.
>
> This code needs to explicitly set the script files executable, due to
> a bug in ant which doesn't allow frobbing of the permission bits when
> we copy the files to the staging directory.
>
> Tests:
>
> Build macOS installer, install it, and successfully compile weather
> demo as swf9 from the dev console.
>
> Testing under windows will need to wait until the builder runs,
> because
> none of us are set up to build windows installers.
>
>
>
> Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/
> SWF9External.java
> ===================================================================
> --- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/
> SWF9External.java 2008-06-23 19:03:53 UTC (rev 9895)
> +++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/
> SWF9External.java 2008-06-23 19:14:19 UTC (rev 9896)
> @@ -18,19 +18,6 @@
> import org.openlaszlo.sc.parser.*;
> import org.openlaszlo.server.LPS;
>
> -/** Flex OEM compiler API
> - * See http://livedocs.adobe.com/flex/3/compilerAPI_flex3.pdf
> - *
> - * source code in flex sources at modules/compiler/src/java/flex2/
> tools/oem/internal/*.java
> - */
> -import flex2.tools.oem.Application;
> -import flex2.tools.oem.Configuration;
> -import flex2.tools.oem.Library;
> -import flex2.tools.oem.Builder;
> -import flex2.tools.oem.Message;
> -import flex2.tools.oem.Logger;
> -
> -
> /**
> * The SWF9External manages communication with the
> * external compiler - generation of source files,
> @@ -243,26 +230,22 @@
> private int origlinenum = -1;
> private int linenum;
> private int colnum;
> -
> - // The error message
> private String error;
> -
> - private String path; // pathname of source file
> private String code = "";
> private String cleanedCode = "";
> -
> + private String orig = "";
> private TranslationUnit tunit;
>
> ExternalCompilerError() {
> - this(null, -1, -1, "", "", "");
> + this(null, -1, -1, "", "");
> }
>
> - ExternalCompilerError(TranslationUnit tunit, int linenum, int
> colnum, String path, String error, String code) {
> + ExternalCompilerError(TranslationUnit tunit, int linenum, int
> colnum, String error, String orig) {
> this.tunit = tunit;
> this.linenum = linenum;
> this.colnum = colnum;
> this.error = error;
> - this.code = this.cleanedCode = code;
> + this.orig = orig;
> }
>
> public String toString() {
> @@ -281,6 +264,11 @@
> return error;
> }
>
> + // returns the original untouched compiler error message
> + public String originalErrorString() {
> + return orig;
> + }
> +
> // returns the complete the compiler error,
> // but without the positional 'caret', and
> // an indication of where the code starts,
> @@ -296,6 +284,21 @@
> return result;
> }
>
> + public void addCodeLine(String str) {
> + if (code.length() > 0) {
> + code += "\n";
> + }
> + code += str;
> +
> + // In cleanedCode, don't keep lines with just spaces and
> caret (^)
> + if (!str.matches("^[ ^]*$")) {
> + if (cleanedCode.length() > 0) {
> + cleanedCode += "\n";
> + }
> + cleanedCode += str;
> + }
> + }
> +
> public String getCode() {
> return code;
> }
> @@ -327,6 +330,74 @@
> }
>
> /**
> + * Collect the error stream, digesting them into individual
> + * ExternalCompilerErrors.
> + */
> + public class ExternalCompilerErrorCollector extends
> OutputCollector {
> +
> + private String inputFilename;
> + private Pattern errPattern;
> + private List errors = new ArrayList();
> + private ExternalCompilerError lastError = null;
> + private TranslationUnit[] tunits;
> +
> + // we don't expect this to be terribly big, can fit in memory
> + StringBuffer sb = new StringBuffer();
> +
> + public ExternalCompilerErrorCollector(InputStream is, List
> tunits) {
> + super(is);
> + this.inputFilename = inputFilename;
> + this.tunits = (TranslationUnit[])tunits.toArray(new
> TranslationUnit[0]);
> +
> + // Expect errors to look like File.as(48): col: 1 Error: some
> message
> +
> + String pat = "([^\\/]+)\\.as\\(([0-9]+)\\): *col: *([0-9]*)
> *(.*)";
> + errPattern = Pattern.compile(pat);
> + //System.out.println("Using error pattern: " + pat);
> + }
> +
> + public TranslationUnit locateTranslationUnit(String nm)
> + {
> + for (int i=0; i<tunits.length; i++) {
> + if (nm.equals(tunits[i].getName()))
> + return tunits[i];
> + }
> + return null;
> + }
> +
> + public void collect(String str) {
> +
> + // We expect errors from this compiler to start with the file
> name.
> + // anything else is just showing us the contents of the line.
> +
> + Matcher matcher = errPattern.matcher(str);
> + if (matcher.find()) {
> + String classnm = matcher.group(1);
> + String linenumstr = matcher.group(2);
> + String colstr = matcher.group(3);
> + TranslationUnit tunit = locateTranslationUnit(classnm);
> + lastError = new ExternalCompilerError(tunit,
> safeInt(linenumstr),
> + safeInt(colstr),
> + matcher.group(4),
> + str);
> + errors.add(lastError);
> + }
> + else {
> + if (lastError == null) {
> + System.err.println("Stray error string from external
> compiler: " + str);
> + // Capture it in an error message not tied to a
> particular line
> + lastError = new ExternalCompilerError();
> + }
> + lastError.addCodeLine(str);
> + }
> + }
> +
> + public List getErrors() {
> + return errors;
> + }
> + }
> +
> + /**
> * True if UNIX quoting rules are in effect.
> */
> public static boolean useUnixQuoting() {
> @@ -360,134 +431,112 @@
> return cmdstr;
> }
>
> -
> - static class FlexLogger implements Logger {
> - private List errors = new ArrayList();
> - private ExternalCompilerError lastError = null;
> - private TranslationUnit[] tunits;
> -
> -
> - // Parse out FILENAME from "FILENAME.as", to get classname
> - static String pat = "([^\\/]+)\\.as";
> - private Pattern errPattern;
> -
> - FlexLogger(List tunits) {
> - this.tunits = (TranslationUnit[])tunits.toArray(new
> TranslationUnit[0]);
> - errPattern = Pattern.compile(pat);
> - }
> -
> - public TranslationUnit locateTranslationUnit(String nm)
> - {
> - for (int i=0; i<tunits.length; i++) {
> - if (nm.equals(tunits[i].getName()))
> - return tunits[i];
> - }
> - return null;
> - }
> -
> - public void log(Message msg, int errorCode, String source) {
> - // errors.add(...)
> - String level = msg.getLevel();
> - String path = msg.getPath();
> - int line = msg.getLine();
> - int column = msg.getColumn();
> -
> - if (path != null) {
> - Matcher matcher = errPattern.matcher(path);
> - if (matcher.find()) {
> - String classnm = matcher.group(1);
> - TranslationUnit tunit = locateTranslationUnit(classnm);
> - lastError = new ExternalCompilerError(tunit, line,
> column, path, msg.toString(), source);
> - errors.add(lastError);
> - }
> - }
> -
> - }
> -
> - public List getErrors() {
> - return errors;
> - }
> -
> -
> - }
> -
> /**
> - * Run the Flex compiler via the Builder API
> + * Run the compiler using the command/arguments in cmd.
> * Collect and report any errors, and check for the existence
> * of the output file.
> * @throw CompilerError if there are errors messages from the
> external
> * compiler, or if any part of the compilation process has
> problems
> */
> - public void buildAndProcessErrors(Builder builder, List tunits,
> String outfilename)
> + public void execCompileCommand(List cmd, String dir, List tunits,
> + String outfileName)
> throws IOException // TODO: [2007-11-20 dda] clean up,
> why catch only some exceptions?
> {
> - FlexLogger logger = new FlexLogger(tunits);
> + String[] cmdstr = (String[])cmd.toArray(new String[0]);
> + String prettycmd = prettyCommand(cmd);
> + System.err.println("Executing compiler: (cd " + dir + "; " +
> prettycmd + ")");
> + String bigErrorString = "";
> + int bigErrorCount = 0;
>
> - // Receives compiler warnings and errors, via the Logger api
> - builder.setLogger(logger);
> + // Generate a small script (unix style) to document how
> + // to build this batch of files.
> + String buildsh = "#!/bin/sh\n";
> + buildsh += "cd " + dir + "\n";
> + buildsh += prettycmd + "\n";
> + Compiler.emitFile(workDirectoryName("build.sh"), buildsh);
>
> - // TODO [2008-06-04 hqm] This is setting "incremental compile" to
> - // true, which won't really help but seems like it can't do any
> - // harm, right? Someday if we use the VirtualLocalFileSystem API
> - // then we could do incremental compiles.
> - long exitval = builder.build(true);
> + Process proc = Runtime.getRuntime().exec(cmdstr, null, null);
> + try {
> + OutputStream os = proc.getOutputStream();
> + OutputCollector outcollect = new
> OutputCollector(proc.getInputStream());
> + ExternalCompilerErrorCollector errcollect = new
> ExternalCompilerErrorCollector(proc.getErrorStream(), tunits);
> + os.close();
> + outcollect.start();
> + errcollect.start();
> + int exitval = proc.waitFor();
> + outcollect.join();
> + errcollect.join();
>
> - String bigErrorString = "";
> - int bigErrorCount = 0;
> + if (outcollect.getException() != null) {
> + System.err.println("Error collecting compiler output: " +
> outcollect.getException());
> + // TODO: [2007-11-20 dda] log this
> + }
> + String compilerOutput = outcollect.getOutput();
> + if (compilerOutput.length() > 0) {
> + System.err.println("compiler output:\n" + compilerOutput);
> + }
>
> - List errs = logger.getErrors();
> - if (errs.size() > 0) {
> - System.err.println("ERRORS: ");
> - for (Iterator iter = errs.iterator(); iter.hasNext(); ) {
> - ExternalCompilerError err =
> (ExternalCompilerError)iter.next();
> - TranslationUnit tunit = err.getTranslationUnit();
> - String srcLineStr;
> - int srcLine;
> + if (errcollect.getException() != null) {
> + System.err.println("Error collecting compiler output: " +
> errcollect.getException());
> + // TODO: [2007-11-20 dda] log this
> + }
> + List errs = errcollect.getErrors();
> + if (errs.size() > 0) {
> + System.err.println("ERRORS: ");
> + for (Iterator iter = errs.iterator(); iter.hasNext(); ) {
> + ExternalCompilerError err =
> (ExternalCompilerError)iter.next();
> + TranslationUnit tunit = err.getTranslationUnit();
> + String srcLineStr;
> + int srcLine;
>
> - // actualSrcLine is the name/linenumber of the actual files
> - // used in compilation, not the original sources.
> - String actualSrcFile = null;
> - if (tunit == null) {
> - actualSrcFile = "(unknown)";
> - }
> - else {
> - actualSrcFile = tunit.getSourceFileName();
> - if (actualSrcFile == null)
> - actualSrcFile = "(" + tunit.getName() + ")";
> - }
> + // actualSrcLine is the name/linenumber of the actual files
> + // used in compilation, not the original sources.
> + String actualSrcFile = null;
> + if (tunit == null) {
> + actualSrcFile = "(unknown)";
> + }
> + else {
> + actualSrcFile = tunit.getSourceFileName();
> + if (actualSrcFile == null)
> + actualSrcFile = "(" + tunit.getName() + ")";
> + }
>
> - String actualSrcLine = "[" + actualSrcFile + ": " +
> err.getLineNumber() + "] ";
> + String actualSrcLine = "[" + actualSrcFile + ": " +
> err.getLineNumber() + "] ";
>
> - if (tunit == null ||
> - ((srcLine =
> tunit.originalLineNumber(err.getLineNumber())) <= 0)) {
> - srcLineStr = "line unknown: ";
> - }
> - else {
> - srcLineStr = "line " + String.valueOf(srcLine) + ": ";
> - }
> - System.err.println(actualSrcLine + srcLineStr +
> err.getErrorString());
> + if (tunit == null ||
> + ((srcLine =
> tunit.originalLineNumber(err.getLineNumber())) <= 0)) {
> + srcLineStr = "line unknown: ";
> + }
> + else {
> + srcLineStr = "line " + String.valueOf(srcLine) + ": ";
> + }
> + System.err.println(actualSrcLine + srcLineStr +
> err.getErrorString());
>
> - // bigErrorString will be passed as an exception.
> - if (bigErrorString.length() > 0) {
> - bigErrorString += "\n";
> + // bigErrorString will be passed as an exception.
> + if (bigErrorString.length() > 0) {
> + bigErrorString += "\n";
> + }
> + bigErrorCount++;
> + if (bigErrorCount < MAX_ERRORS_SHOWN) {
> + bigErrorString += srcLineStr + err.cleanedErrorString();
> + }
> + else if (bigErrorCount == 50) {
> + bigErrorString += ".... more than " + MAX_ERRORS_SHOWN +
> + " errors, additional errors not shown.";
> + }
> }
> - bigErrorCount++;
> - if (bigErrorCount < MAX_ERRORS_SHOWN) {
> - bigErrorString += srcLineStr + err.cleanedErrorString();
> - }
> - else if (bigErrorCount == 50) {
> - bigErrorString += ".... more than " + MAX_ERRORS_SHOWN +
> - " errors, additional errors not shown.";
> - }
> }
> - }
>
> - if (!(exitval > 0)) {
> - System.err.println("FAIL: compiler returned " + exitval);
> + if (exitval != 0) {
> + System.err.println("FAIL: compiler returned " + exitval);
> + }
> }
> + catch (InterruptedException ie) {
> + throw new CompilerError("Interrupted compiler");
> + }
> System.err.println("Done executing compiler");
> - if (!new File(outfilename).exists()) {
> - System.err.println("Intermediate file " + outfilename + ":
> does not exist");
> + if (!new File(outfileName).exists()) {
> + System.err.println("Intermediate file " + outfileName + ":
> does not exist");
> if (bigErrorString.length() > 0) {
> throw new CompilerError(bigErrorString);
> }
> @@ -540,71 +589,60 @@
> public byte[] compileTranslationUnits(List tunits, boolean
> buildSharedLibrary)
> throws IOException
> {
> + List cmd = new ArrayList();
> String outfilebase;
> - Builder builder;
> - ArrayList files = new ArrayList();
> + String exeSuffix = isWindows() ? ".exe" : "";
> +
> + if (buildSharedLibrary) {
> + outfilebase = "app.swc";
> + cmd.add(getFlexPathname("bin/compc" + exeSuffix));
> + }
> + else {
> + outfilebase = "app.swf";
> + cmd.add(getFlexPathname("bin/mxmlc" + exeSuffix));
> + }
>
> - // Collect up the file (or files, for a library) we are compiling
> + String outfilename = workdir.getPath() + File.separator +
> outfilebase;
> + boolean swf9Warnings = getLPSBoolean("compiler.swf9.warnings",
> true);
> +
> + if (!swf9Warnings) {
> + cmd.add("-compiler.show-actionscript-warnings=false");
> + }
> +
> + cmd.add("-compiler.source-path+=" + workdir.getPath());
> + if (USE_COMPILER_DEBUG_FLAG) {
> + cmd.add("-debug=true");
> + }
> + cmd.add("-output");
> + cmd.add(outfilename);
> +
> + if (!buildSharedLibrary) {
> + cmd.add("-default-size");
> + cmd.add(options.get(Compiler.CANVAS_WIDTH, "800"));
> + cmd.add(options.get(Compiler.CANVAS_HEIGHT, "600"));
> + cmd.add("-library-path+=" + getLFCLibrary());
> + }
> + else {
> + // must be last before list of classes to follow.
> + cmd.add("-include-classes");
> + }
> +
> for (Iterator iter = tunits.iterator(); iter.hasNext(); ) {
> TranslationUnit tunit = (TranslationUnit)iter.next();
> +
> // For the application, we just list the main .as file
> // For a library, we list all the classes.
> if (!buildSharedLibrary) {
> if (tunit.isMainTranslationUnit()) {
> - files.add(new File(workdir.getPath() + File.separator +
> tunit.getName()+".as"));
> + cmd.add(workdir.getPath() + File.separator +
> tunit.getName()+".as");
> }
> - } else {
> - files.add(new File(tunit.getName()));
> }
> - }
> -
> - // Set compiler config options
> - Configuration config;
> - String outfilename;
> -
> - if (buildSharedLibrary) {
> - // For a library, add all the 'components'
> - builder = new Library();
> - config = builder.getDefaultConfiguration();
> - for (int i = 0; i < files.size(); i++) {
> - ((Library)builder).addComponent(((File)
> (files.get(i))).getName());
> + else {
> + cmd.add(tunit.getName());
> }
> - outfilebase = "app.swc";
> - outfilename = workdir.getPath() + File.separator +
> outfilebase;
> - ((Library)builder).setOutput(new File(outfilename));
> }
> - else {
> - // For an application, compile just one 'main' class
> - builder = new Application((File) files.get(0));
> - config = builder.getDefaultConfiguration();
> - outfilebase = "app.swf";
> - outfilename = workdir.getPath() + File.separator +
> outfilebase;
> - ((Application)builder).setOutput(new File(outfilename));
> -
> - }
> -
> - boolean swf9Warnings = getLPSBoolean("compiler.swf9.warnings",
> true);
> -
> - // set reporting of warnings
> - config.showActionScriptWarnings(swf9Warnings);
> - config.showBindingWarnings(swf9Warnings);
> - config.showShadowedDeviceFontWarnings(swf9Warnings);
> - config.showUnusedTypeSelectorWarnings(swf9Warnings);
> -
> - // Append path using the 'source-path+=' option
> - config.addSourcePath(new File[] {workdir});
> -
> - config.enableDebugging(USE_COMPILER_DEBUG_FLAG, "");
>
> - if (!buildSharedLibrary) {
> - config.setDefaultSize(safeInt((String)
> (options.get(Compiler.CANVAS_WIDTH, "800"))),
> - safeInt((String)
> (options.get(Compiler.CANVAS_HEIGHT, "600"))));
> -
> - config.addLibraryPath(new File[] {new File(getLFCLibrary())});
> - }
> -
> - builder.setConfiguration(config);
> - buildAndProcessErrors(builder, tunits, outfilename);
> + execCompileCommand(cmd, workdir.getPath(), tunits, outfilename);
> return getBytes(outfilename);
> }
>
>
> Modified: openlaszlo/trunk/installer/macosx/OpenLaszlo
> Explorer.command.proto
> ===================================================================
> --- openlaszlo/trunk/installer/macosx/OpenLaszlo
> Explorer.command.proto 2008-06-23 19:03:53 UTC (rev 9895)
> +++ openlaszlo/trunk/installer/macosx/OpenLaszlo
> Explorer.command.proto 2008-06-23 19:14:19 UTC (rev 9896)
> @@ -1,10 +1,11 @@
> #!/bin/bash
> # * P_LZ_COPYRIGHT_BEGIN
> ******************************************************
> -# * Copyright 2001-2004 Laszlo Systems, Inc. All Rights
> Reserved. *
> +# * Copyright 2001-2004, 2008 Laszlo Systems, Inc. All Rights
> Reserved. *
> # * Use is subject to license
> terms. *
> # * P_LZ_COPYRIGHT_END
> ********************************************************
> my_home=`dirname "$0"`
> tomcat_home=$my_home/Server/@TOMCAT@
> +export FLEX_HOME="$my_home/Server/lps- at VERSIONID@/WEB-INF"
> url=`echo file://${my_home}/Server/lps-@VERSIONID@/lps/utils/startup-
> static.html | sed "s/ /%20/g"`
> open $url
> env JAVA_OPTS="-Dcom.apple.backgroundOnly=true -
> Djava.awt.headless=true" JAVA_HOME=/usr "${tomcat_home}/bin/
> catalina.sh" run
>
> Modified: openlaszlo/trunk/installer/macosx/Start OpenLaszlo
> Server.command.proto
> ===================================================================
> --- openlaszlo/trunk/installer/macosx/Start OpenLaszlo
> Server.command.proto 2008-06-23 19:03:53 UTC (rev 9895)
> +++ openlaszlo/trunk/installer/macosx/Start OpenLaszlo
> Server.command.proto 2008-06-23 19:14:19 UTC (rev 9896)
> @@ -1,9 +1,10 @@
> #!/bin/bash
> # * P_LZ_COPYRIGHT_BEGIN
> ******************************************************
> -# * Copyright 2001-2004 Laszlo Systems, Inc. All Rights
> Reserved. *
> +# * Copyright 2001-2004, 2008 Laszlo Systems, Inc. All Rights
> Reserved. *
> # * Use is subject to license
> terms. *
> # * P_LZ_COPYRIGHT_END
> ********************************************************
> my_home=`dirname "$0"`
> -tomcat_home=$my_home/Server/@TOMCAT@
> +tomcat_home="$my_home/Server/@TOMCAT@"
> +export FLEX_HOME="$my_home/Server/lps- at VERSIONID@/WEB-INF"
> url=`echo file://${my_home}/lps-@VERSIONID@/lps/utils/startup-
> static.html | sed "s/ /%20/g"`
> env JAVA_OPTS="-Dcom.apple.backgroundOnly=true -
> Djava.awt.headless=true" JAVA_HOME=/usr "${tomcat_home}/bin/
> catalina.sh" run
>
> Modified: openlaszlo/trunk/installer/macosx/mkpkg.sh
> ===================================================================
> --- openlaszlo/trunk/installer/macosx/mkpkg.sh 2008-06-23 19:03:53
> UTC (rev 9895)
> +++ openlaszlo/trunk/installer/macosx/mkpkg.sh 2008-06-23 19:14:19
> UTC (rev 9896)
> @@ -1,6 +1,6 @@
> #!/bin/sh
> # * P_LZ_COPYRIGHT_BEGIN
> ******************************************************
> -# * Copyright 2001-2004 Laszlo Systems, Inc. All Rights
> Reserved. *
> +# * Copyright 2001-2004, 2008 Laszlo Systems, Inc. All Rights
> Reserved. *
> # * Use is subject to license
> terms. *
> # * P_LZ_COPYRIGHT_END
> ********************************************************
>
> @@ -14,6 +14,11 @@
> PKG="$BASE.pkg"
> NAME="OpenLaszlo Server $VERSION"
>
> +# Make flex utils and LPS utils executable
> +# because ant won't do it for us.
> +/bin/chmod a+x "$LPS_HOME/lps-$VERSION/Server/lps-$VERSION/WEB-INF/
> bin/"*
> +/bin/chmod a+x "$LPS_HOME/lps-$VERSION/Server/lps-$VERSION/WEB-INF/
> lps/server/bin/"*
> +
> /bin/rm -rf /tmp/$PKG
> /bin/mkdir /tmp/$PKG
> /bin/mkdir /tmp/$PKG/Contents
>
>
> _______________________________________________
> Laszlo-checkins mailing list
> Laszlo-checkins at openlaszlo.org
> http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins
More information about the Laszlo-dev
mailing list