Skip to content

Commit

Permalink
Add AUTO_DETECT_MAIN setting
Browse files Browse the repository at this point in the history
This settings, when enabled, forces the developer to be explicit
about the presence or absence of `main` their program.  It is disabled
by default but enabled in STRICT mode.

In this mode, if you want to build a program without a `main` function
you must either build with `--no-entry` or pass a list of
EXPORTED_FUNCTIONS the doesn't include `_main`.

The reason for adding the extra `--no-entry` option is that this
is what `wasm-ld` recommends in its corresponding error message.  Its
also a lot easier to type than `-s EXPORTED_FUNCTIONS=[]`.

This change is a step towards enabling better error reporting from lld
by enabling the removal of the `--allow-undefined` option.
  • Loading branch information
sbc100 committed Apr 3, 2020
1 parent 3369df9 commit 9deb5c8
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 8 deletions.
8 changes: 8 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ def __init__(self):
# Whether we will expand the full path of any input files to remove any
# symlinks.
self.expand_symlinks = True
self.no_entry = False


def use_source_map(options):
Expand Down Expand Up @@ -1180,6 +1181,9 @@ def get_last_setting_change(setting):
# Apply -s settings in newargs here (after optimization levels, so they can override them)
apply_settings(settings_changes)

if options.no_entry and '_main' in shared.Settings.EXPORTED_FUNCTIONS:
shared.Settings.EXPORTED_FUNCTIONS.remove('_main')

shared.verify_settings()

def filter_out_dynamic_libs(inputs):
Expand Down Expand Up @@ -1214,6 +1218,7 @@ def check(input_file):
shared.Settings.STRICT_JS = 1
shared.Settings.AUTO_JS_LIBRARIES = 0
shared.Settings.AUTO_ARCHIVE_INDEXES = 0
shared.Settings.IGNORE_MISSING_MAIN = 0

# If set to 1, we will run the autodebugger (the automatic debugging tool, see
# tools/autodebugger). Note that this will disable inclusion of libraries. This
Expand Down Expand Up @@ -2914,6 +2919,9 @@ def consume_arg():
options.shell_path = consume_arg()
elif check_arg('--source-map-base'):
options.source_map_base = consume_arg()
elif newargs[i] == '--no-entry':
options.no_entry = True
newargs[i] = ''
elif check_arg('--js-library'):
shared.Settings.SYSTEM_JS_LIBRARIES.append(os.path.abspath(consume_arg()))
elif newargs[i] == '--remove-duplicates':
Expand Down
5 changes: 5 additions & 0 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,11 @@ def report_missing_symbols(all_implemented, pre):
continue
diagnostics.warning('undefined', 'undefined exported function: "%s"', requested)

# Handle main specially, unless IGNORE_MISSING_MAIN is set
if not shared.Settings.IGNORE_MISSING_MAIN:
if '_main' in shared.Settings.EXPORTED_FUNCTIONS and '_main' not in all_implemented:
exit_with_error('entry symbol not defined (pass --no-entry to suppress): main')


def get_exported_implemented_functions(all_exported_functions, all_implemented, metadata):
funcs = set(metadata['exports'])
Expand Down
7 changes: 7 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -941,11 +941,18 @@ var LINKABLE = 0;
// * The C define EMSCRIPTEN is not defined (__EMSCRIPTEN__ always is, and
// is the correct thing to use).
// * STRICT_JS is enabled.
// * IGNORE_MISSING_MAIN is disabled.
// * AUTO_JS_LIBRARIES is disabled.
// * AUTO_ARCHIVE_INDEXES is disabled.
// [compile+link]
var STRICT = 0;

// Allow program to link with or without `main` symbol.
// If this is disabled then one must provide a `main` symbol or expclictly
// opt out by passing `--no-entry` or an EXPORTED_FUNCTIONS list that doesn't
// include `_main`.
var IGNORE_MISSING_MAIN = 1;

// Automatically attempt to add archive indexes at link time to archives that
// don't already have them. This can happen when GNU ar or GNU ranlib is used
// rather than `llvm-ar` or `emar` since the former don't understand the wasm
Expand Down
30 changes: 23 additions & 7 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1613,17 +1613,17 @@ def test_inherit(self):
self.do_run_in_out_file_test('tests', 'core', 'test_inherit')

def test_isdigit_l(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_isdigit_l')
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_isdigit_l')

def test_iswdigit(self):
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_iswdigit')
# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME', 1)
self.do_run_in_out_file_test('tests', 'core', 'test_iswdigit')

def test_polymorph(self):
self.do_run_in_out_file_test('tests', 'core', 'test_polymorph')
self.do_run_in_out_file_test('tests', 'core', 'test_polymorph')

def test_complex(self):
self.do_run_in_out_file_test('tests', 'core', 'test_complex')
Expand Down Expand Up @@ -8862,6 +8862,22 @@ def test_minimal_runtime_emscripten_get_exported_function(self):
self.emcc_args += ['-lexports.js', '-s', 'MINIMAL_RUNTIME=1']
self.do_run_in_out_file_test('tests', 'core', 'test_get_exported_function')

def test_auto_detect_main(self):
self.do_run_in_out_file_test('tests', 'core', 'test_ctors_no_main')

# Disabling IGNORE_MISSING_MAIN should cause link to fail due to missing main
self.set_setting('IGNORE_MISSING_MAIN', 0)
err = self.expect_fail([PYTHON, EMCC, path_from_root('tests', 'core', 'test_ctors_no_main.cpp')] + self.get_emcc_args())
self.assertContained('error: entry symbol not defined (pass --no-entry to suppress): main', err)

# We can fix the error either by adding --no-entry or by setting EXPORTED_FUNCTIONS to empty
self.emcc_args.append('--no-entry')
self.do_run_in_out_file_test('tests', 'core', 'test_ctors_no_main')

self.emcc_args.remove('--no-entry')
self.set_setting('EXPORTED_FUNCTIONS', [])
self.do_run_in_out_file_test('tests', 'core', 'test_ctors_no_main')


# Generate tests for everything
def make_run(name, emcc_args, settings=None, env=None):
Expand Down
7 changes: 6 additions & 1 deletion tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -1812,8 +1812,13 @@ def link_lld(args, target, opts=[], all_external_symbols=None):
'--initial-memory=%d' % Settings.INITIAL_MEMORY,
]
use_start_function = Settings.STANDALONE_WASM

if not use_start_function:
cmd += ['--no-entry']
has_main = '_main' in Settings.EXPORTED_FUNCTIONS
if has_main and not Settings.IGNORE_MISSING_MAIN:
cmd += ['--entry=main']
else:
cmd += ['--no-entry']
if not Settings.ALLOW_MEMORY_GROWTH:
cmd.append('--max-memory=%d' % Settings.INITIAL_MEMORY)
elif Settings.MAXIMUM_MEMORY != -1:
Expand Down

0 comments on commit 9deb5c8

Please sign in to comment.