Skip to content

Commit b8e5396

Browse files
authored
Simplify/refactor handling of main/_start (#12020)
This change is NFC for non-standalone mode. For standalone more we no longer have any special handling for `main`. This is because `_start` is the entry symbol for standalone mode. We no longer include `_main` in EXPORTED_FUNCTIONS by default. With this change `--no-entry` is only way build a reactor. EXPORTED_FUNCTIONS no longer effects whether we build a reactor or a command.
1 parent 274434c commit b8e5396

File tree

9 files changed

+47
-42
lines changed

9 files changed

+47
-42
lines changed

ChangeLog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ See docs/process.md for how version tagging works.
1717

1818
Current Trunk
1919
-------------
20+
- `--no-entry` is now required in `STANDALONE_WASM` mode when building a reactor
21+
(application without a main function). Previously exporting a list of
22+
functions that didn't include `_main` would imply this. Now the list of
23+
`EXPORTED_FUNCTIONS` is not relevant in the deciding the type of application
24+
to build.
2025
- Allow polymorphic types to be used without RTTI when using embind. (#10914)
2126
- Only strip the LLVM producer's section in release builds. In `-O0` builds, we
2227
try to leave the wasm from LLVM unmodified as much as possible, so if it

emcc.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -906,10 +906,10 @@ def need_llvm_debug_info(options):
906906

907907
newargs = [arg for arg in newargs if arg]
908908

909-
settings_key_changes = set()
909+
settings_key_changes = {}
910910
for s in settings_changes:
911911
key, value = s.split('=', 1)
912-
settings_key_changes.add(key)
912+
settings_key_changes[key] = value
913913

914914
# Find input files
915915

@@ -1037,12 +1037,9 @@ def add_link_flag(i, f):
10371037
# Libraries are searched before settings_changes are applied, so apply the
10381038
# value for STRICT from command line already now.
10391039

1040-
def get_last_setting_change(setting):
1041-
return ([None] + [x for x in settings_changes if x.startswith(setting + '=')])[-1]
1042-
1043-
strict_cmdline = get_last_setting_change('STRICT')
1040+
strict_cmdline = settings_key_changes.get('STRICT')
10441041
if strict_cmdline:
1045-
shared.Settings.STRICT = int(strict_cmdline.split('=', 1)[1])
1042+
shared.Settings.STRICT = int(strict_cmdline)
10461043

10471044
# Apply optimization level settings
10481045
shared.Settings.apply_opt_level(opt_level=shared.Settings.OPT_LEVEL, shrink_level=shared.Settings.SHRINK_LEVEL, noisy=True)
@@ -1056,12 +1053,6 @@ def get_last_setting_change(setting):
10561053
# Remove the default exported functions 'malloc', 'free', etc. those should only be linked in if used
10571054
shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = []
10581055

1059-
# Remove the default _main function from shared.Settings.EXPORTED_FUNCTIONS.
1060-
# We do this before the user settings are applied so it affects the default value only and a
1061-
# user could use `--no-entry` and still export main too.
1062-
if options.no_entry:
1063-
shared.Settings.EXPORTED_FUNCTIONS.remove('_main')
1064-
10651056
# Apply -s settings in newargs here (after optimization levels, so they can override them)
10661057
apply_settings(settings_changes)
10671058

@@ -1145,9 +1136,24 @@ def get_last_setting_change(setting):
11451136
# we also do not support standalone mode in fastcomp.
11461137
shared.Settings.STANDALONE_WASM = 1
11471138

1148-
if options.no_entry or ('_main' not in shared.Settings.EXPORTED_FUNCTIONS and
1149-
'__start' not in shared.Settings.EXPORTED_FUNCTIONS):
1139+
if options.no_entry:
11501140
shared.Settings.EXPECT_MAIN = 0
1141+
elif shared.Settings.STANDALONE_WASM:
1142+
if '_main' in shared.Settings.EXPORTED_FUNCTIONS:
1143+
# TODO(sbc): Make this into a warning?
1144+
logger.debug('including `_main` in EXPORTED_FUNCTIONS is not necessary in standalone mode')
1145+
else:
1146+
# In normal non-standalone mode we have special handling of `_main` in EXPORTED_FUNCTIONS.
1147+
# 1. If the user specifies exports, but doesn't include `_main` we assume they want to build a
1148+
# reactor.
1149+
# 2. If the user doesn't export anything we default to exporting `_main` (unless `--no-entry`
1150+
# is specified (see above).
1151+
if 'EXPORTED_FUNCTIONS' in settings_key_changes:
1152+
if '_main' not in shared.Settings.USER_EXPORTED_FUNCTIONS:
1153+
shared.Settings.EXPECT_MAIN = 0
1154+
else:
1155+
assert(not shared.Settings.EXPORTED_FUNCTIONS)
1156+
shared.Settings.EXPORTED_FUNCTIONS = ['_main']
11511157

11521158
if shared.Settings.STANDALONE_WASM:
11531159
# In STANDALONE_WASM mode we either build a command or a reactor.

emscripten.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,15 +265,9 @@ def apply_table(js):
265265

266266

267267
def report_missing_symbols(all_implemented, pre):
268-
required_symbols = set(shared.Settings.USER_EXPORTED_FUNCTIONS)
269-
# In standalone mode a request for `_main` is interpreted as a request for `_start`
270-
# so don't warn about mossing main.
271-
if shared.Settings.STANDALONE_WASM and '_main' in required_symbols:
272-
required_symbols.discard('_main')
273-
274268
# the initial list of missing functions are that the user explicitly exported
275269
# but were not implemented in compiled code
276-
missing = list(required_symbols - all_implemented)
270+
missing = list(set(shared.Settings.USER_EXPORTED_FUNCTIONS) - all_implemented)
277271

278272
for requested in missing:
279273
if ('function ' + asstr(requested)) in pre:

src/settings.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -762,14 +762,13 @@ var NODE_CODE_CACHING = 0;
762762

763763
// Functions that are explicitly exported. These functions are kept alive
764764
// through LLVM dead code elimination, and also made accessible outside of the
765-
// generated code even after running closure compiler (on "Module"). Note the
766-
// necessary prefix of "_".
765+
// generated code even after running closure compiler (on "Module"). The
766+
// symbols listed here require an `_` prefix.
767767
//
768-
// Note also that this is the full list of exported functions - if you have a
769-
// main() function and want it to run, you must include it in this list (as
770-
// _main is by default in this value, and if you override it without keeping it
771-
// there, you are in effect removing it).
772-
var EXPORTED_FUNCTIONS = ['_main'];
768+
// By default if this setting is not specified on the command line the
769+
// `_main` function will be implicitly exported. In STANDALONE_WASM mode the
770+
// default export is `__start` (or `__initialize` if --no-entry is specified).
771+
var EXPORTED_FUNCTIONS = [];
773772

774773
// If true, we export all the symbols that are present in JS onto the Module
775774
// object. This does not affect which symbols will be present - it does not

tests/test_core.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8293,14 +8293,16 @@ def test_undefined_main(self):
82938293
err = self.expect_fail([EMCC, path_from_root('tests', 'core', 'test_ctors_no_main.cpp')] + self.get_emcc_args())
82948294
self.assertContained('error: entry symbol not defined (pass --no-entry to suppress): main', err)
82958295

8296+
# In non-standalone mode exporting an empty list of functions signal that we don't
8297+
# have a main and so should not generate an error.
8298+
self.set_setting('EXPORTED_FUNCTIONS', [])
8299+
self.do_run_in_out_file_test('tests', 'core', 'test_ctors_no_main.cpp')
8300+
self.clear_setting('EXPORTED_FUNCTIONS')
8301+
82968302
# If we pass --no-entry or set EXPORTED_FUNCTIONS to empty should never see any errors
82978303
self.emcc_args.append('--no-entry')
82988304
self.do_run_in_out_file_test('tests', 'core', 'test_ctors_no_main.cpp')
82998305

8300-
self.emcc_args.remove('--no-entry')
8301-
self.set_setting('EXPORTED_FUNCTIONS', [])
8302-
self.do_run_in_out_file_test('tests', 'core', 'test_ctors_no_main.cpp')
8303-
83048306
def test_export_start(self):
83058307
if not can_do_standalone(self):
83068308
self.skipTest('standalone mode only')

tests/test_other.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9664,10 +9664,7 @@ def test_autoconf_mode(self):
96649664
self.assertContained('hello, world!', output)
96659665

96669666
def test_standalone_export_main(self):
9667-
# Tests that explictly exported `_main` does not fail. Since we interpret an
9668-
# export of `_main` to be be an export of `__start` in standalone mode the
9669-
# actual main function is not exported, but we also don't want to report an
9670-
# error
9671-
self.set_setting('STANDALONE_WASM')
9672-
self.set_setting('EXPORTED_FUNCTIONS', ['_main'])
9673-
self.do_run_in_out_file_test('tests', 'core', 'test_hello_world.c')
9667+
# Tests that explicitly exported `_main` does not fail, even though `_start` is the entry
9668+
# point.
9669+
# We should consider making this a warning since the `_main` export is redundant.
9670+
self.run_process([EMCC, '-sEXPORTED_FUNCTIONS=[_main]', '-sSTANDALONE_WASM', '-c', path_from_root('tests', 'core', 'test_hello_world.c')])

tools/building.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,6 @@ def lld_flags_for_executable(external_symbol_list):
511511
if external_symbol_list:
512512
# Filter out symbols external/JS symbols
513513
c_exports = [e for e in c_exports if e not in external_symbol_list]
514-
if Settings.STANDALONE_WASM and Settings.EXPECT_MAIN and 'main' in c_exports:
515-
c_exports.remove('main')
516514
for export in c_exports:
517515
cmd += ['--export', export]
518516

tools/shared.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
diagnostics.add_warning('undefined', error=True)
6464
diagnostics.add_warning('deprecated')
6565
diagnostics.add_warning('version-check')
66+
diagnostics.add_warning('export-main')
6667
diagnostics.add_warning('unused-command-line-argument', shared=True)
6768

6869

tools/system_libs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,9 @@ class libjsmath(Library):
13651365
# If main() is not in EXPORTED_FUNCTIONS, it may be dce'd out. This can be
13661366
# confusing, so issue a warning.
13671367
def warn_on_unexported_main(symbolses):
1368+
# In STANDALONE_WASM we don't expect main to be explictly exported
1369+
if shared.Settings.STANDALONE_WASM:
1370+
return
13681371
if '_main' not in shared.Settings.EXPORTED_FUNCTIONS:
13691372
for symbols in symbolses:
13701373
if 'main' in symbols.defs:

0 commit comments

Comments
 (0)