Skip to content

Commit 0cce6f8

Browse files
authored
Default to EXECUTABLE=1 when generating a JS file with no extension (#26088)
Followup to #26085 which added initial support for `-sEXECUTABLE`. The assumption here is that if we are generating a file with no extension (or something called `a.out`) then we are trying to build something that is directly runnable.
1 parent d3d9e0a commit 0cce6f8

File tree

5 files changed

+40
-8
lines changed

5 files changed

+40
-8
lines changed

ChangeLog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ See docs/process.md for more on how version tagging works.
2222
-----------------------
2323
- compiler-rt was updated to LLVM 21.1.8. (#26405)
2424
- A new `-sEXECUTABLE` setting was added which adds a #! line to the resulting
25-
JavaScript and makes it executable. (#26085)
25+
JavaScript and makes it executable. This setting defaults to true when the
26+
output filename has no extension, or ends in `.out` (e.g. `a.out`) (#26085)
2627

2728
4.0.23 - 01/10/26
2829
-----------------

docs/emcc.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,11 @@ Options that are modified or new in *emcc* are listed below:
585585
These rules only apply when linking. When compiling to object code
586586
(See *-c* below) the name of the output file is irrelevant.
587587

588+
Note: Linking to a file with no extension (or a file ending in
589+
".out", like "a.out") will cause the generated JavaScript file to
590+
be exectuable, and include a "#!" line to make it runnable
591+
directly.
592+
588593
"-c"
589594
[compile] Tells *emcc* to emit an object file which can then be
590595
linked with other object files to produce an executable.

site/source/docs/tools_reference/emcc.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ Options that are modified or new in *emcc* are listed below:
574574
These rules only apply when linking. When compiling to object code (See `-c`
575575
below) the name of the output file is irrelevant.
576576

577+
Note: Linking to a file with no extension (or a file ending in ``.out``, like
578+
``a.out``) will cause the generated JavaScript file to be exectuable, and
579+
include a ``#!`` line to make it runnable directly.
580+
577581
.. _emcc-c:
578582

579583
``-c``

test/test_other.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15283,14 +15283,28 @@ def test_binary_encode(self, extra):
1528315283
write_file('test.js', read_file(path_from_root('src/binaryDecode.js')) + '\nvar src = ' + binary_encoded + ';\n' + test_js)
1528415284
self.assertContained('OK', self.run_js('test.js'))
1528515285

15286-
@no_windows('depends on UNIX shbang feature')
15287-
def test_executable(self):
15288-
# First test without -sEXECUTABLE
15286+
@crossplatform
15287+
def test_executable_output_default(self):
15288+
# By default there should not be a #! line included
1528915289
self.run_process([EMCC, test_file('hello_world.c')])
15290+
self.assertFalse(read_file('a.out.js').startswith('#!'))
1529015291
self.assertNotContained('#!/usr/bin/env node', read_file('a.out.js'))
1529115292

15292-
# Now, test with -sEXECUTABLE
15293-
self.run_process([EMCC, test_file('hello_world.c'), '-sEXECUTABLE'])
15294-
self.assertContained('#!/usr/bin/env node', read_file('a.out.js'))
15295-
output = self.run_process([os.path.abspath('a.out.js')], stdout=PIPE).stdout
15293+
@crossplatform
15294+
@no_windows('depends on UNIX shbang feature')
15295+
@parameterized({
15296+
'': (['-sEXECUTABLE', '-o', 'out.js'],),
15297+
# -sEXECUTABLE is implied if output filename has no extension
15298+
'no_extension': (['-o', 'foo'],),
15299+
# -sEXECUTABLE is implied for `.out` extension, e.g. a.out.
15300+
'a_out': (['-o', 'a.out'],),
15301+
})
15302+
def test_executable_output(self, args):
15303+
js_filename = args[-1]
15304+
self.run_process([EMCC, test_file('hello_world.c')] + args)
15305+
self.assertContained('#!/usr/bin/env node', read_file(js_filename))
15306+
output = self.run_process([os.path.abspath(js_filename)], stdout=PIPE).stdout
1529615307
self.assertContained('hello, world!', output)
15308+
15309+
def test_executable_requires_node(self):
15310+
self.assert_fail([EMCC, test_file('hello_world.c'), '-sEXECUTABLE', '-sENVIRONMENT=web'], 'emcc: error: EXECUTABLE requires `node` in ENVRIONMENT')

tools/link.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,14 @@ def phase_linker_setup(options, linker_args): # noqa: C901, PLR0912, PLR0915
931931
if s in user_settings:
932932
diagnostics.warning('unused-command-line-argument', f'{s} is only valid when generating JavaScript output')
933933

934+
# When there is no final suffix or the suffix is `.out` (as in `a.out`) then default to
935+
# making the resulting file exectuable.
936+
if settings.ENVIRONMENT_MAY_BE_NODE and options.oformat == OFormat.JS and final_suffix in ('', '.out'):
937+
default_setting('EXECUTABLE', 1)
938+
939+
if settings.EXECUTABLE and not settings.ENVIRONMENT_MAY_BE_NODE:
940+
exit_with_error('EXECUTABLE requires `node` in ENVRIONMENT')
941+
934942
if options.oformat == OFormat.MJS:
935943
default_setting('EXPORT_ES6', 1)
936944

0 commit comments

Comments
 (0)