It depends on what you mean by "native executable".
If you mean "runs natively on the hardware", then no: if you don't have machine code, then you don't have machine code, and a runtime is required.
If you mean "can be launched from the operating system in the same way that a machine-code-based executable is", then yes - perhaps even trivially. But at this point there isn't a clear distinction between "running an 'executable' file that requires a specific runtime" and "using the runtime to open the file". Which is to say, in Windows I could trivially associate .py
or .pyc
files with a Python interpreter, type their name as a "command" in CMD or double-click an icon in Explorer, and have the code execute - but few people would call this "making the file executable" even though the user experience may be identical to running a .exe
.
Linux, on the other hand, doesn't have the same concept of file association (although a desktop environment might build a reasonable facsimile on top). Instead, you set an executable permissions bit on the file, and then arrange the file format such that it has a signature that is recognized by binfmt_misc
.
If you mean "is stored in a container format that the OS commonly uses for machine-code-based executables" - e.g., as ELF on Linux or PE on Windows - yes, it's certainly possible. The .NET platform works this way: code in .NET languages such as C# compiles to bytecode that may end up stored in a file in the PE format - to my understanding, it's stored as "data", and the "code" is a hard-coded bootstrap which makes a system call to locate the runtime and feed the bytecode to it. A similar trick should be equally possible with ELF. (Of course, as you've noted, you could also just put the entire interpreter there, and not worry about needing to find it on the system. Many "Python standalone app builder" utilities work this way.)
On Linux, that approach has the advantage that the system is already configured to recognize the ELF format and "execute" it by loading the machine code.
In any event, if you want such an "executable" to also be usable by the interpreter, you just need to arrange for the interpreter to recognize the "executable format". The trivial example of this is supporting #
as a comment character, so that valid source code can have a shebang line; then the interpreter sees ordinary code, while Linux sees an "executable text file" that is "executed" by parsing the shebang to find the interpreter and passing the script to it. A more complex example is Python bytecode: binfmt_misc
may be configured to recognize .pyc
files according to the header bytes that specify the bytecode version (mapping to a corresponding interpreter).
Related reading: File extensions and association with programs in linux on unix.SE
#!/usr/bin/your-interpreter
line at the start of the file, and setting its+x
flag. This is how e.g. Python works, though you put the hashbang in the source code rather than the compiled bytecode. $\endgroup$