Skip to content

Commit

Permalink
Add option to not default for C++ (which matches clang/gcc behaviour) (
Browse files Browse the repository at this point in the history
  • Loading branch information
sbc100 committed Apr 11, 2020
1 parent 593c1ee commit ddc6c94
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 41 deletions.
2 changes: 1 addition & 1 deletion em++.py
Expand Up @@ -7,7 +7,7 @@
import sys
import emcc

sys.argv += ['--emscripten-cxx']
emcc.run_via_emxx = True

if __name__ == '__main__':
try:
Expand Down
65 changes: 32 additions & 33 deletions emcc.py
Expand Up @@ -584,6 +584,9 @@ def is_supported(f):
return results


run_via_emxx = False


#
# Main run() function
#
Expand All @@ -608,12 +611,6 @@ def run(args):
if DEBUG and LEAVE_INPUTS_RAW:
logger.warning('leaving inputs raw')

if '--emscripten-cxx' in args:
run_via_emxx = True
args = [x for x in args if x != '--emscripten-cxx']
else:
run_via_emxx = False

misc_temp_files = shared.configuration.get_temp_files()

# Handle some global flags
Expand Down Expand Up @@ -701,13 +698,6 @@ def run(args):
print(' '.join(shared.Building.doublequote_spaces(parts[1:])))
return 0

# Default to using C++ even when run as `emcc`.
# This means that emcc will act as a C++ linker when no source files are
# specified. However, when a C source is specified we do default to C.
# This differs to clang and gcc where the default is always C unless run as
# clang++/g++.
use_cxx = True

def get_language_mode(args):
return_next = False
for item in args:
Expand All @@ -720,20 +710,8 @@ def get_language_mode(args):
return item[2:]
return None

def has_c_source(args):
for a in args:
if a[0] != '-' and a.endswith(C_ENDINGS + OBJC_ENDINGS):
return True
return False

language_mode = get_language_mode(args)
has_fixed_language_mode = language_mode is not None
if language_mode == 'c':
use_cxx = False

if not has_fixed_language_mode:
if not run_via_emxx and has_c_source(args):
use_cxx = False

def is_minus_s_for_emcc(args, i):
# -s OPT=VALUE or -s OPT are interpreted as emscripten flags.
Expand All @@ -750,7 +728,7 @@ def is_minus_s_for_emcc(args, i):
# If this is a configure-type thing, do not compile to JavaScript, instead use clang
# to compile to a native binary (using our headers, so things make sense later)
CONFIGURE_CONFIG = (os.environ.get('EMMAKEN_JUST_CONFIGURE') or 'conftest.c' in args) and not os.environ.get('EMMAKEN_JUST_CONFIGURE_RECURSE')
CMAKE_CONFIG = 'CMakeFiles/cmTryCompileExec.dir' in ' '.join(args)# or 'CMakeCCompilerId' in ' '.join(args)
CMAKE_CONFIG = 'CMakeFiles/cmTryCompileExec.dir' in ' '.join(args)
if CONFIGURE_CONFIG or CMAKE_CONFIG:
# XXX use this to debug configure stuff. ./configure's generally hide our
# normal output including stderr so we write to a file
Expand All @@ -773,7 +751,7 @@ def is_minus_s_for_emcc(args, i):

# if CONFIGURE_CC is defined, use that. let's you use local gcc etc. if you need that
compiler = os.environ.get('CONFIGURE_CC') or shared.EMXX
if 'CXXCompiler' not in ' '.join(args) and not use_cxx:
if run_via_emxx:
compiler = shared.to_cc(compiler)

def filter_emscripten_options(argv):
Expand Down Expand Up @@ -914,13 +892,8 @@ def need_llvm_debug_info(options):

options, settings_changes, newargs = parse_args(newargs)

if use_cxx:
clang_compiler = CXX
else:
clang_compiler = CC

if '-print-search-dirs' in newargs:
return run_process([clang_compiler, '-print-search-dirs'], check=False).returncode
return run_process([CC, '-print-search-dirs'], check=False).returncode

if options.emrun:
options.pre_js += open(shared.path_from_root('src', 'emrun_prejs.js')).read() + '\n'
Expand Down Expand Up @@ -1231,6 +1204,7 @@ def check(input_file):
shared.Settings.AUTO_JS_LIBRARIES = 0
shared.Settings.AUTO_ARCHIVE_INDEXES = 0
shared.Settings.IGNORE_MISSING_MAIN = 0
shared.Settings.DEFAULT_TO_CXX = 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 @@ -1302,6 +1276,27 @@ def check(input_file):
# such as LTO and SIDE_MODULE/MAIN_MODULE effect which cache directory we use.
shared.reconfigure_cache()

def has_c_source(args):
for a in args:
if a[0] != '-' and a.endswith(C_ENDINGS + OBJC_ENDINGS):
return True
return False

use_cxx = False
if has_fixed_language_mode:
if 'c++' in language_mode:
use_cxx = True
elif run_via_emxx:
use_cxx = True
elif shared.Settings.DEFAULT_TO_CXX and not has_c_source(args):
# Default to using C++ even when run as `emcc`.
# This means that emcc will act as a C++ linker when no source files are
# specified.
# This differs to clang and gcc where the default is always C unless run as
# clang++/g++.
use_cxx = True
shared.Settings.USE_CXX = use_cxx

if not compile_only:
ldflags = shared.emsdk_ldflags(newargs)
for f in ldflags:
Expand Down Expand Up @@ -2077,6 +2072,10 @@ def check_human_readable_list(items):

try:
with ToolchainProfiler.profile_block('compile inputs'):
if use_cxx:
clang_compiler = CXX
else:
clang_compiler = CC
# Precompiled headers support
if has_header_inputs:
headers = [header for _, header in input_files]
Expand Down
6 changes: 6 additions & 0 deletions src/settings.js
Expand Up @@ -951,6 +951,7 @@ var LINKABLE = 0;
// * IGNORE_MISSING_MAIN is disabled.
// * AUTO_JS_LIBRARIES is disabled.
// * AUTO_ARCHIVE_INDEXES is disabled.
// * DEFAULT_TO_CXX is disabled.
// [compile+link]
var STRICT = 0;

Expand Down Expand Up @@ -1783,6 +1784,11 @@ var USE_OFFSET_CONVERTER = 0;
// [link]
var LLD_REPORT_UNDEFINED = 0;

// Default to c++ mode even when run as `emcc` rather then `emc++`.
// When this is disabled `em++` is required when compiling and linking C++
// programs. This which matches the behaviour of gcc/g++ and clang/clang++.
var DEFAULT_TO_CXX = 1;

//===========================================
// Internal, used for testing only, from here
//===========================================
Expand Down
3 changes: 3 additions & 0 deletions src/settings_internal.js
Expand Up @@ -188,3 +188,6 @@ var SEPARATE_DWARF = 0;

// New WebAssembly exception handling (experimental)
var EXCEPTION_HANDLING = 0;

// Enabled when building C++ code (for example via em++ or via -c c++)
var USE_CXX = 0;
2 changes: 1 addition & 1 deletion tests/core/test_double_i64_conversion.c
Expand Up @@ -5,7 +5,7 @@
* found in the LICENSE file.
*/

#include <cassert>
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>

Expand Down
16 changes: 11 additions & 5 deletions tests/runner.py
Expand Up @@ -59,7 +59,7 @@
sys.path.append(__rootpath__)

import parallel_runner
from tools.shared import EM_CONFIG, TEMP_DIR, EMCC, DEBUG, PYTHON, LLVM_TARGET, ASM_JS_TARGET, EMSCRIPTEN_TEMP_DIR, WASM_TARGET, SPIDERMONKEY_ENGINE, WINDOWS, EM_BUILD_VERBOSE
from tools.shared import EM_CONFIG, TEMP_DIR, EMCC, EMXX, DEBUG, PYTHON, LLVM_TARGET, ASM_JS_TARGET, EMSCRIPTEN_TEMP_DIR, WASM_TARGET, SPIDERMONKEY_ENGINE, WINDOWS, EM_BUILD_VERBOSE
from tools.shared import asstr, get_canonical_temp_dir, Building, run_process, try_delete, to_cc, asbytes, safe_copy, Settings
from tools import jsrun, shared, line_endings

Expand Down Expand Up @@ -620,18 +620,24 @@ def build(self, src, dirname, filename, main_file=None,
os.chdir(self.get_dir())

suffix = '.o.js' if js_outfile else '.o.wasm'
all_sources = [filename] + additional_files
if any(os.path.splitext(s)[1] in ('.cc', '.cxx', '.cpp') for s in all_sources):
compiler = EMXX
else:
compiler = EMCC

if build_ll_hook:
# "slow", old path: build to bc, then build to JS

# C++ => LLVM binary

for f in [filename] + additional_files:
for f in all_sources:
try:
# Make sure we notice if compilation steps failed
os.remove(f + '.o')
except OSError:
pass
args = [PYTHON, EMCC] + self.get_emcc_args(main_file=True) + \
args = [PYTHON, compiler] + self.get_emcc_args(main_file=True) + \
['-I' + dirname, '-I' + os.path.join(dirname, 'include')] + \
['-I' + include for include in includes] + \
['-c', f, '-o', f + '.o']
Expand All @@ -655,12 +661,12 @@ def build(self, src, dirname, filename, main_file=None,
Building.emcc(object_file, self.get_emcc_args(main_file=True), object_file + '.js')
else:
# "fast", new path: just call emcc and go straight to JS
all_files = [filename] + additional_files + libraries
all_files = all_sources + libraries
for i in range(len(all_files)):
if '.' not in all_files[i]:
shutil.move(all_files[i], all_files[i] + '.bc')
all_files[i] += '.bc'
args = [PYTHON, EMCC] + self.get_emcc_args(main_file=True) + \
args = [PYTHON, compiler] + self.get_emcc_args(main_file=True) + \
['-I' + dirname, '-I' + os.path.join(dirname, 'include')] + \
['-I' + include for include in includes] + \
all_files + ['-o', filename + suffix]
Expand Down
1 change: 1 addition & 0 deletions tests/test_core.py
Expand Up @@ -8994,6 +8994,7 @@ def setUp(self):
# wasm
wasm2s = make_run('wasm2s', emcc_args=['-O2'], settings={'SAFE_HEAP': 1})
wasm2ss = make_run('wasm2ss', emcc_args=['-O2'], settings={'STACK_OVERFLOW_CHECK': 2})
strict = make_run('strict', emcc_args=['-O2'], settings={'DEFAULT_TO_CXX': 0})

if not shared.Settings.WASM_BACKEND:
# emterpreter
Expand Down
23 changes: 23 additions & 0 deletions tests/test_other.py
Expand Up @@ -10746,3 +10746,26 @@ def test_argument_match(self):
def test_missing_argument(self):
err = self.expect_fail([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '--js-opts'])
self.assertContained("error: option '--js-opts' requires an argument", err)

def test_default_to_cxx(self):
create_test_file('foo.h', '#include <string.h>')
create_test_file('cxxfoo.h', '#include <string>')

# The default bahviour is to default to C++, which means the C++ header can be compiled even
# with emcc.
run_process([PYTHON, EMCC, '-c', 'cxxfoo.h'])

# But this means that C flags can't be passed (since we are assuming C++)
err = self.expect_fail([PYTHON, EMCC, '-std=gnu11', '-c', 'foo.h'])
self.assertContained("'-std=gnu11' not allowed with 'C++'", err)

# If we disable DEFAULT_TO_CXX the emcc can be used with cflags, but can't be used to build
# C++ headers
run_process([PYTHON, EMCC, '-std=gnu11', '-c', 'foo.h', '-s', 'DEFAULT_TO_CXX=0'])
err = self.expect_fail([PYTHON, EMCC, '-c', 'cxxfoo.h', '-s', 'DEFAULT_TO_CXX=0'])
self.assertContained("'string' file not found", err)

# Using em++ should alwasy work for C++ headers
run_process([PYTHON, EMXX, '-c', 'cxxfoo.h', '-s', 'DEFAULT_TO_CXX=0'])
# Or using emcc with `-x c++`
run_process([PYTHON, EMCC, '-c', 'cxxfoo.h', '-s', 'DEFAULT_TO_CXX=0', '-x', 'c++-header'])
4 changes: 3 additions & 1 deletion tools/shared.py
Expand Up @@ -916,7 +916,9 @@ def include_directive(paths):
return result

# libcxx include paths must be defined before libc's include paths otherwise libcxx will not build
return c_opts + include_directive(cxx_include_paths) + include_directive(c_include_paths)
if Settings.USE_CXX:
c_opts += include_directive(cxx_include_paths)
return c_opts + include_directive(c_include_paths)


def get_cflags(user_args):
Expand Down

0 comments on commit ddc6c94

Please sign in to comment.