Index: test/org/jruby/test/TestCommandlineParser.java =================================================================== --- test/org/jruby/test/TestCommandlineParser.java (revision 24caafc5efafa556b1d41c3ea0b69186dae648a1) +++ test/org/jruby/test/TestCommandlineParser.java Thu Jan 14 19:09:41 CET 2010 @@ -114,4 +114,12 @@ assertEquals("--command", parser.getArgv()[0]); assertEquals("irb", parser.getArgv()[1]); } + + public void testCommandExtractScript() { + String[] args = new String[] { "-xtmp", "testDoc.foo"}; + RubyInstanceConfig parser = new RubyInstanceConfig(); + parser.processArguments(args); + assertEquals(true, parser.isxFlag()); + assertEquals("testDoc.foo", parser.getScriptFileName()); -} + } +} Index: src/org/jruby/Main.java =================================================================== --- src/org/jruby/Main.java (revision 24caafc5efafa556b1d41c3ea0b69186dae648a1) +++ src/org/jruby/Main.java Thu Jan 14 19:09:58 CET 2010 @@ -43,9 +43,9 @@ import java.io.InputStreamReader; import java.io.PrintStream; import java.io.File; - import java.io.FileInputStream; import java.io.FileNotFoundException; + import org.jruby.exceptions.MainExitException; import org.jruby.exceptions.RaiseException; import org.jruby.exceptions.ThreadKill; @@ -90,9 +90,9 @@ System.err.println("Fatal: GCJ (GNU Compiler for Java) is not supported by JRuby."); System.exit(1); } - + Main main = new Main(); - + try { int status = main.run(args); if (status != 0) { @@ -126,7 +126,7 @@ } catch (OutOfMemoryError oome) { // produce a nicer error since Rubyists aren't used to seeing this System.gc(); - + String memoryMax = SafePropertyAccessor.getProperty("jruby.memory.max"); String message = ""; if (memoryMax != null) { @@ -134,7 +134,7 @@ } config.getError().println("Error: Your application used more memory than the safety cap" + message + "."); config.getError().println("Specify -J-Xmx####m to increase it (#### = cap size in MB)."); - + if (config.getVerbose()) { config.getError().println("Exception trace follows:"); oome.printStackTrace(); @@ -145,7 +145,7 @@ } catch (StackOverflowError soe) { // produce a nicer error since Rubyists aren't used to seeing this System.gc(); - + String stackMax = SafePropertyAccessor.getProperty("jruby.stack.max"); String message = ""; if (stackMax != null) { @@ -153,7 +153,7 @@ } config.getError().println("Error: Your application used more stack memory than the safety cap" + message + "."); config.getError().println("Specify -J-Xss####k to increase it (#### = cap size in KB)."); - + if (config.getVerbose()) { config.getError().println("Exception trace follows:"); soe.printStackTrace(); @@ -164,7 +164,7 @@ } catch (UnsupportedClassVersionError ucve) { config.getError().println("Error: Some library (perhaps JRuby) was built with a later JVM version."); config.getError().println("Please use libraries built with the version you intend to use or an earlier one."); - + if (config.getVerbose()) { config.getError().println("Exception trace follows:"); ucve.printStackTrace(); @@ -181,12 +181,12 @@ if (config.isShowVersion()) { showVersion(); } - + if (config.isShowCopyright()) { showCopyright(); } - if (!config.shouldRunInterpreter() ) { + if (!config.shouldRunInterpreter()) { if (config.shouldPrintUsage()) { printUsage(); } @@ -217,6 +217,9 @@ if (in == null) { // no script to run, return success below + } else if (config.isxFlag() && !config.hasShebangLine()) { + // no shebang was found and x option is set + throw new MainExitException(1, "jruby: no Ruby script found in input (LoadError)"); } else if (config.isShouldCheckSyntax()) { int status = 0; try { @@ -230,7 +233,7 @@ throw re; } } - + if (config.getArgv().length > 0) { for (String arg : config.getArgv()) { File argFile = new File(arg); @@ -312,7 +315,7 @@ hasPrintedUsage = true; } } - + public void printProperties() { config.getOutput().print(config.getPropertyHelp()); } @@ -337,8 +340,17 @@ in.mark(1024); reader = new BufferedReader(new InputStreamReader(in, "iso-8859-1"), 8192); String firstLine = reader.readLine(); + + // Search for the shebang line in the given stream + // if it wasn't found on the first line and the -x option + // was specified + if (config.isxFlag()) { + while (firstLine != null && !isShebangLine(firstLine)) { + firstLine = reader.readLine(); + } + } boolean usesEnv = false; - if (firstLine.length() > 2 && firstLine.charAt(0) == '#' && firstLine.charAt(1) == '!') { + if (isShebangLine(firstLine)) { String[] options = firstLine.substring(2).split("\\s+"); int i; for (i = 0; i < options.length; i++) { @@ -360,7 +372,11 @@ break; } } + config.setHasShebangLine(true); System.arraycopy(options, i, result, 0, options.length - i); + } else { + // No shebang line found + config.setHasShebangLine(false); } } catch (Exception ex) { // ignore error @@ -371,4 +387,8 @@ } return result; } + + protected boolean isShebangLine(String line) { + return (line.length() > 2 && line.charAt(0) == '#' && line.charAt(1) == '!'); -} + } +} Index: src/org/jruby/RubyInstanceConfig.java =================================================================== --- src/org/jruby/RubyInstanceConfig.java (revision 24caafc5efafa556b1d41c3ea0b69186dae648a1) +++ src/org/jruby/RubyInstanceConfig.java Thu Jan 14 19:09:41 CET 2010 @@ -28,14 +28,7 @@ ***** END LICENSE BLOCK *****/ package org.jruby; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; +import java.io.*; import java.net.URI; import java.nio.channels.FileChannel; import java.util.ArrayList; @@ -76,15 +69,17 @@ * The JIT threshold to the specified method invocation count. */ private static final int JIT_THRESHOLD = 50; - + - /** The version to use for generated classes. Set to current JVM version by default */ + /** + * The version to use for generated classes. Set to current JVM version by default + */ public static final int JAVA_VERSION; - + /** * Default size for chained compilation. */ private static final int CHAINED_COMPILE_LINE_COUNT_DEFAULT = 500; - + /** * The number of lines at which a method, class, or block body is split into * chained methods (to dodge 64k method-size limit in JVM). @@ -92,6 +87,28 @@ public static final int CHAINED_COMPILE_LINE_COUNT = SafePropertyAccessor.getInt("jruby.compile.chainsize", CHAINED_COMPILE_LINE_COUNT_DEFAULT); + /** + * Indicates whether the script must be extracted from script source + */ + private boolean xFlag; + + public boolean hasShebangLine() { + return hasShebangLine; + } + + public void setHasShebangLine(boolean hasShebangLine) { + this.hasShebangLine = hasShebangLine; + } + + /** + * Indicates whether the script has a shebang line or not + */ + private boolean hasShebangLine; + + public boolean isxFlag() { + return xFlag; + } + public enum CompileMode { JIT, FORCE, OFF; @@ -123,7 +140,7 @@ = SafePropertyAccessor.getBoolean("jruby.objectspace.enabled", false); private CompileMode compileMode = CompileMode.JIT; - private boolean runRubyInProcess = true; + private boolean runRubyInProcess = true; private String currentDirectory; private Map environment; private String[] argv = {}; @@ -165,7 +182,7 @@ private boolean endOfArguments = false; private boolean shouldRunInterpreter = true; private boolean shouldPrintUsage = false; - private boolean shouldPrintProperties=false; + private boolean shouldPrintProperties = false; private KCode kcode = KCode.NONE; private String recordSeparator = "\n"; private boolean shouldCheckSyntax = false; @@ -276,7 +293,7 @@ nativeEnabled = false; specVersion = "1.5"; } - + if (specVersion.equals("1.5")) { JAVA_VERSION = Opcodes.V1_5; } else { @@ -301,7 +318,7 @@ runRubyInProcess = parentConfig.runRubyInProcess; excludedMethods = parentConfig.excludedMethods; threadDumpSignal = parentConfig.threadDumpSignal; - + classCache = new ClassCache