JDK-8201275 : Launch Single-File Source-Code Programs
  • Type: CSR
  • Component: tools
  • Sub-Component: launcher
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Submitted: 2018-04-06
  • Updated: 2023-10-31
  • Resolved: 2018-06-07
Related Reports
CSR :  
Relates :  
Description
Summary
-------

Update the Java launcher to support launching single-file
source-code programs.

Problem
-------

(For full details and examples, see [JEP 330](http://openjdk.java.net/jeps/330)).

The need to perform separate "compile" and "execute" steps
for simple programs makes it harder to learn how to use
Java, and harder to deploy simple programs as executable code.


Solution
--------

Update the Java launcher to be able to launch single file programs,
by recognizing the use of a source file name on the command line,
so that it can be compiled and subsequently executed.

In addition, update the launcher to make it easier to leverage the
"shebang" mechanism often found on Unix-derived systems to
execute single-file programs.

Discussion
-----------

The solution introduces a new `--source` _version_ option to
force source-file mode and/or to specify the version of the source
language and API used in the source file. As part of the operation
of source-file mode, an implicit `--release` _version_ option
is passed to the internal invocation of the platform compiler, javac.

The javac `--release` option is closely related to the long-standing
javac `-source`  option, and so there is some risk of confusion
between the new launcher `--source`  option and the javac
`-source` option, for those who are familiar with the latter.

It has been considered to rename or alias the new launcher 
`--source` option as `--release`, leveraging the analogy with
the javac option. However, while a `--source` option for the
launcher can be neatly described as enabling "source-file mode"
there is no such easy explanation for `--release`. Furthermore,
introducing `--release` _version_ as a launcher option
might lead users to believe it has an impact on the runtime
when it is used, which is definitely not the case. 

Additionally, the use of `--release` as a launcher option, 
albeit with different semantics, has already been considered and 
rejected. (See [JEP 12](http://openjdk.java.net/jeps/12).)

Finally, unlike all other options that are supported both by
the launcher and by the compiler (including all the path-related 
options and module system options), the new launcher option
is not a direct analog of either the javac `-source` option or
`--release` option, which significantly reduces the incentive
to choose either one for that reason.

The decision is therefore to use `--source` as the least
confusing of the two alternatives.

Specification
-------------

The change is a combination of two parts:

1. An enhancement to the interpretation of what previously has been the 
"main class" on the command line.
2. A new `--source` _version_ option

First, the launcher identifies the first argument (if any) on the command line 
that is neither an option nor a part of an option ... i.e. the argument that previously
has always identified the name of the main class to be invoked.

Then, if that argument identifies an existing file with an extension of `.java`
it is assumed to identify a source file.

Or, if the argument identifies an existing file and a new `--source` _version_ option
is also present on the command line, the argument is assumed to be a source file 
regardless of the file's extension. The _version_ identifies the language version of 
the source code in the file.

If either of the preceding conditions identify the argument as a source file,
the launcher proceeds in source-file mode as follows:

In source-file mode, any additional command-line options are processed as follows:

* The launcher scans the options specified before the source file for any that are
relevant in order to compile the source file.  This includes: `--class-path`,
`--module-path`, `--add-exports`, `--add-modules`, `--limit-modules`,
`--patch-module`, `--upgrade-module-path`, and any variant forms of those
options. It also includes the new `--enable-preview` option, described in
[JEP 12](http://openjdk.java.net/jeps/12).

* No provision is made to pass any additional options to the compiler,
such as `-processor` or `-Werror`.

* [Command-line argument files](https://docs.oracle.com/javase/10/tools/java.htm#JSWOR-GUID-4856361B-8BFD-4964-AE84-121F5F6CF111) (@-files)
may be used in the standard way. Long lists of arguments for either the VM or
the program being invoked may be placed in files which are specified on the
command-line by prefixing the filename with an `@` character.

In source-file mode, compilation proceeds as follows:

* Any command-line options that are relevant to the compilation environment are
taken into account.

* No other source files are found and compiled, as if the source path is set to
an empty value.

* Annotation processing is disabled, as if `-proc:none` is in effect.

* If a _version_ is specified, the value is used as the argument for an implicit 
`--release` option for the compilation. This sets both the source version 
accepted by compiler and the system API that may be used by the code in the 
source file.

* The source file is compiled in the context of an unnamed module.

* The source file should contain one or more top-level classes, 
the first of which is taken as the class to be executed.

* The compiler does not enforce the optional restriction defined at the end of
[JLS §7.6](https://docs.oracle.com/javase/specs/jls/se9/html/jls-7.html#jls-7.6),
that a type in a named package should exist in a file whose name is composed
from the type name followed by the `.java` extension.

* If the source file contains errors, appropriate error messages are written
to the standard error stream, and the launcher exits with a non-zero exit code.

In source-file mode, execution proceeds as follows:

* The class to be executed is the first top-level class found in the source
file. It must contain a declaration of the standard
`public static void main(String[])` method.

* The compiled classes are loaded by a custom class loader, that delegates to
the application class loader. (This implies that classes appearing on the
application class path cannot refer to any classes declared in the source
file.)

* The compiled classes are executed in the context of an unnamed module, and
 as if `--add-modules=ALL-DEFAULT` is in effect (in addition to any other
 `--add-module` options that may be have been specified on the command line.)

* Any arguments appearing after the name of the file on the command line are
 passed to the standard `main` method in the obvious way.

* It is an error if there is a class on the application class path whose name
is the same as that of the class to be executed.

Note that there is a potential minor ambiguity when using a simple command-line
like `java HelloWorld.java`. Previously, `HelloWorld.java` would have been
interpreted as a class called `java` in a package called `HelloWorld`, but which
is now resolved in favor of a file called `HelloWorld.java` if such a file
exists. 

The new `--source` _version_ option, and the enhanced behavior for determining
the main class to be executed, do not affect the VM or JNI interface. 

### Command-line help

The primary command-line help, generated by `--help`, is modified as follows:

    7a8
    >    or  java [options] source-file [args]
    9,10c10,12
    <  Arguments following the main class, -jar <jarfile>, -m or --module
    <  <module>/<mainclass> are passed as the arguments to main class.
    ---
    >  Arguments following the main class, source-file, -jar <jarfile>,
    >  -m or --module <module>/<mainclass> are passed as the arguments to
    >  main class.

The additional command-line help, generated by `--help-extra`, is modified as follows:

    58a59,60
    >     --source <version>
    >                       specify the Java language version expected in source-file mode


### "Shebang" files

["Shebang" files](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) are
a mechanism provided by some Unix-derived systems to execute a file
with an executable named in the first line of the file, which must begin
with the two ASCII characters `#!`. Any content on the first line after the 
name of the executable is typically passed as an initial argument, along
with the name of the file, to the nominated executable.

To support this mechanism for Java source files, the source-code launcher
makes two accommodations:

1. When the launcher reads the source file, if the first line begins with 
ASCII `#!`, then the contents of that line up to but not including the first 
newline are ignored when determining the source code to be passed to the compiler.

2. Some operating systems pass the text on the first line after the name of the 
executable as a single argument to the executable. 
With that in mind, if the launcher encounters an option beginning `--source` 
and containing whitespace, it is split into a series of words, separated by whitespace, 
before being further analyzed by the launcher. 


Comments
Moving to Approved.
07-06-2018

There is historically overlap in some of the options between java and javac, primarily those that configure the various search paths. The basic interface of this JEP is a helpful "do what I mean," if the java command is presented with a *.java file, compile and run it. If additional compilation options are to be provided, the most direct analog on javac today is "--release N". A "--source" option is close, but not identical to, the long-standing javac "-source" option. The proposed "--source" option may be more intuitive to more casual javac users, but I think it is slightly confusing to those familiar with javac's "-source".
24-04-2018

I think the way to "spin" this is that the proposed new `--source` _version_ option is to enable _source-file mode_ ... a significant term that is introduced in the JEP and here in the CSR. In source-file mode, a number of javac options will or may be set: * --release _version_ * -proc:none * -sourcepath "" * --enable-preview (if set on the command line) * additional options from the command line that are relevant to the compilation (the list is already specified in the CSR) With all that in mind, the new `--source` option is more than just a perceived similarity to the underlying javac `-source` option. It is also more than the underlying javac `--release` option, which is just one aspect of the new source-file mode.
18-04-2018

I don't think there's a lot of value to supporting `--release` as an alias. From the user perspective, this is about launching _source_ files; you say `--source` because you want to force the "launch source" mode. I also worry that supporting the alias is a slippery slope to having javac flags intrude on the launcher.
18-04-2018

Regarding (2) There is no underlying VM invocation. There is only the initial VM invocation which is used to run javac (via the javax.compiler API) and then the class to be executed (in a class loader). Therefore the --enable-preview will inherently be handled in the VM. However, it is true that it should additionally be handled by the compiler, so that the compilation environment matches the runtime environment.
18-04-2018

After discussion with Jon, moving this request to Provisional to indicate it has gone through initial CSR review, but additional details remain to be worked out. Two of those details: 1) For consistency with the javac command line, should --release be accepted as an alias for the described semantics of --source (which make --source an alias for --release in javac)? 2) How should the --enable-preview option be handled? Also passed through to the underlying compile and VM invocations?
17-04-2018

FWIW, --module-path never ever allows source files to be provided. It might be reasonable to specify that various paths are set to empty, to clarify that the default behavior to have paths default to the class path does not apply. It would be harmless, and clear, if we said that -proc:none was in play, as compared to inferring that no processors could be run because paths were empty, and -processor cannot be specified. It is less helpful to specify -implicit:none is in play, because that only affects the generation of class files; it does not prevent javac from trying to read source files. This implies that _if_ -sourcepath were allowed to be set, we would be able to _compile_ the source file, but not subsequently _execute_ it (because the implicitly compiled class files would not be available.) That doesn't help anyone, and so it is better to prevent the file being compiled in the first place. As to why we don't allow source files on the classpath, that would open up a different can of worms, since the class path in question is the JVM classpath, specified as an argument to the Java launcher. If you want that level of flexibility, you are beyond the use cases for this feature and should be using traditional javac/java commands instead.
17-04-2018

Right; I was thinking of the situation of " --class-path, --module-path" being set and then having those treated as a source-path entries where additional source files could come from. I'd be happy to advance the CSR in its current form to Provisional, but I'd like some more time to think about potential interactions.
17-04-2018

No, since there is no way for javac to access any other source files for implicit compilation, there is no need for such an option. A different but arguably more pertitent question would be: what about environment options like --source-path and -processorpath, which default to the classpath if not set explicitly? These should both be set to "empty". Setting the sourcepath for the internal compilation to empty prevents reading other source files. Setting the processorpath for the internal compilation to empty, and by not permitting options like -processor or -Xplug to be specified, prevents annotation processing from running and therefore generating additional source and class files.
16-04-2018

Per "No other source files are compiled." is -implicit:none passed done to the underlying compilation?
16-04-2018