-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
emscripten_strict #4665
emscripten_strict #4665
Conversation
Need to run full suite before merging, but wanted to post this already for initial comments. Also needs a good heads up conversation on the mailing list, but let's first discuss here that the different deprecations are something that we want to agree on, and what would be the best way to bring those in. (In one go like this, or separately?) |
Overall this sounds good. Main concerns are, as you said, to not surprise people and discuss everything well in advance, and yeah, a mailing list post to point to here makes sense.
How about |
Alright. In the first version/sdk release, let's land this in a manner that does not remove any features, but just adds the strict mode. And in a later release, do the actual removal. That would give the kind of "two stage deprecate->remove" process to ensure not too abrupt changes. I'll post to the mailing list about this one to raise awareness. |
if not use_js: | ||
cmd += shared.EMSDK_OPTS + ['-D__EMSCRIPTEN__'] | ||
# The preprocessor define EMSCRIPTEN is deprecated. Don't pass it to code in strict mode. Code should use the define __EMSCRIPTEN__ instead. | ||
if not os.environ.get('EMSCRIPTEN_STRICT') or int(os.environ.get('EMSCRIPTEN_STRICT')) == 0: cmd += ['-DEMSCRIPTEN'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be nicer to have a variable strict_mode
or such, that does the environment check once at the top of the file.
in fact maybe it could be in tools/shared.py
, as others might need it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
COMPILER_OPTS = COMPILER_OPTS + ['-D__EMSCRIPTEN__'] | ||
|
||
# The preprocessor define EMSCRIPTEN is deprecated. Don't pass it to code in strict mode. Code should use the define __EMSCRIPTEN__ instead. | ||
if not os.environ.get('EMSCRIPTEN_STRICT') or int(os.environ.get('EMSCRIPTEN_STRICT')) == 0: COMPILER_OPTS += ['-DEMSCRIPTEN'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes, it is needed here. so maybe have Building.is_strict()
, in this file, and we call it from all the places that need to ask if we are in strict mode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added shared.is_emscripten_strict()
. Could not put it in Building
, because that is too late in the file for early bits of the Settings-related code to refer to it (and reordering would create other kind of dependency issue, with Building referring to Settings too early), so made it a self-standing function at the top of the file.
Meanwhile reading this a second time, I do see the logic in calling the option |
+1, this is great; it should result in more consistent (and therefore hopefully less surprising) behavior, and allow us more implementation flexibility. |
I think it helps to have the linker flag |
Well, this would be the first env var that has the exact same name as a setting - which opens up opportunities for confusion. Actually, though, why do we need both an env var and a setting here? |
I'd like to give as much flexibility as possible to be able to easily configure the feature to builds. Env. vars are great for locally/temporarily setting a value to try out once, but linker flags are better for build systems. |
Are you suggesting we temporarily support the two ways, or forever? |
7665d8b
to
8f9f7e9
Compare
Ok, renamed to env. var |
logging.warning('emcc: cannot find library "%s"', lib) | ||
if not found: | ||
# Some native libraries are implemented in Emscripten as system side JS libraries | ||
js_system_libraries = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about refactoring this js system lib logic into Building
in tools/shared.py
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code is dependant on settings_changes
before applying them, since it's filtering out which command line link items to accept/fail on. I'm not sure why it would be useful to separate it out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it's not code likely to be used elsewhere. But just imagine adding a js library in the future, it feels nicer to edit a small function in shared.py
rather than some code in the middle of emcc.py
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, moved the code to Building
.
@@ -931,8 +935,10 @@ def get_llvm_target(): | |||
|
|||
if LLVM_TARGET == WASM_TARGET: | |||
# wasm target does not automatically define emscripten stuff, so do it here. | |||
COMPILER_OPTS = COMPILER_OPTS + ['-DEMSCRIPTEN', | |||
'-D__EMSCRIPTEN__'] | |||
COMPILER_OPTS = COMPILER_OPTS + ['-D__EMSCRIPTEN__'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like this setting of the emscripten define is duplicated in emcc.py
? if we need it in both (but do we?), perhaps we could refactor it into a shared method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, the machinery touches the old EMCONFIGURE_JS
hacks that are used to support the ./configure
architecture. Looks like that mode does not have __EMSCRIPTEN__
set, as it shouldn't(?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I see, the hacks there need to only add it in some cases.
I suppose we could have a add_emscripten_define
method somewhere, which returns the one define or both depending on if we are in strict mode. How does that sound?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if adding that is much worth it, since this occurs exactly in two places, and it's quite lean to just add it in these locations. Eventually we'll be tearing down the code anyways when we get to remove #define EMSCRIPTEN
altogether, so building a new abstraction for this seems temporary? Left this one as is for now, let me know if you feel strongly about this.
@@ -471,6 +471,10 @@ def find_temp_directory(): | |||
def get_emscripten_version(path): | |||
return open(path).read().strip().replace('"', '') | |||
|
|||
# Returns true if Emscripten is running in 'strict' mode, in which deprecated compiler features are not supported. | |||
def is_emscripten_strict(): | |||
return (os.environ.get('EMCC_STRICT') and int(os.environ.get('EMCC_STRICT')) != 0) or Settings.STRICT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can try to access Settings.STRICT
before the Settings
object exists, that worries me a lot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In particular, we should have tests that check usage with both the env var and without, of things both working and not, so we hit that code path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hit that case, and had to adapt.
b71f229
to
862c9aa
Compare
Pushed some new changes, namely made the |
862c9aa
to
70f2b71
Compare
Updated the PR and rebased to latest. This should be good to go now. |
if not found: | ||
system_js_libraries += shared.Building.path_to_system_js_libraries(lib) | ||
|
||
# Certain linker flags imply some link libraries to be pulled in by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i still feel these lines are nicer in something like shared.Building.add_default_system_libraries_from_settings_changes
(or a better name ;) ), but i don't feel strongly about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe leave that for a possible followup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, added a function for that in Building
. Not sure if I managed a better name though :)
$FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', '$WORKERFS', 'stdin', 'stdout', 'stderr'], | ||
$FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', | ||
#if __EMSCRIPTEN_HAS_idbfs_js__ | ||
'$IDBFS', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation not consistent with the following ones
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
// building with -s ERROR_ON_MISSING_LIBRARIES=0 setting, | ||
// prefer to set that option explicitly in your build system. | ||
|
||
var SYSTEM_JS_LIBRARIES = []; // Specifies a list of Emscripten-provided JS libraries to link against. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently we have js_libraries
in emcc.py
, which invokes emscripten.py
with --libraries
. It seems like emcc.py
could use this new variable, and we could get rid of --libraries
? That would avoid duplicated functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although for people using emscripten.py
directly, maybe we shouldn't remove that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, that might be true, though not sure if I want to change this right now at least, there doesn't seem to be any tests for the --libraries
flag in our test suite.
@@ -472,6 +472,15 @@ def find_temp_directory(): | |||
def get_emscripten_version(path): | |||
return open(path).read().strip().replace('"', '') | |||
|
|||
# Returns true if Emscripten is running in 'strict' mode, in which deprecated compiler features are not supported. | |||
def is_emscripten_strict(): | |||
if os.environ.get('EMCC_STRICT') and int(os.environ.get('EMCC_STRICT')) != 0: return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this int
operation may throw, if the value is e.g. a
. instead, please do os.environ.get('EMCC_STRICT') != '0'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's good to throw, no? That would indicate someone messed up setting the env. var, so a critical abort there would make sense, as opposed to e.g. silently treating export EMCC_STRICT=a
as falsey or something similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throwing might be ok, but it'll throw something like ValueError: invalid literal for int() with base 10
which is not very clear. and in all other places, we accept '0'
as "no", and everything else, including "a"
as yes.
'-Dunix', | ||
'-D__unix', | ||
'-D__unix__'] | ||
|
||
# The preprocessor define EMSCRIPTEN is deprecated. Don't pass it to code in strict mode. Code should use the define __EMSCRIPTEN__ instead. | ||
if not is_emscripten_strict(): COMPILER_OPTS += ['-DEMSCRIPTEN'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we discussed this already - this code happens before the STRICT
setting is applied. so only the env var can influence this I think? ditto below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's right. Revised the code flow for this now.
Some testing comments:
|
Added test
Added test |
When testing, I realize that the preprocessor define check goes all the way to emscripten-core/emscripten-fastcomp-clang#11 |
Thanks, lgtm aside from that tiny test thing. |
Oh and the |
… libraries, but instead require explicitly specifying which ones to link to on command line.
…ripten.h> and #include <emscripten/emscripten.h> can be used in the future.
…uite to pass in EMCC_STRICT mode.
…ding in tools/shared.py.
… code path in shared.Building.
13ccbfe
to
08b70e5
Compare
Changed the '0' thing and rebased to latest. This would be good to merge in before merge to master. Existing behavior should not be changed in any way, so should be safe to add in. |
@kripken: Does this look good to you to merge? I'd really like to get this in to 1.37.0, because this adds the new |
@@ -918,6 +922,24 @@ def detect_fixed_language_mode(args): | |||
if separate_asm: | |||
shared.Settings.SEPARATE_ASM = os.path.basename(asm_target) | |||
|
|||
if 'EMCC_STRICT' in os.environ: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this happens after the code above for -DEMSCRIPTEN
, so it seems it would only work for the setting and not the environment var?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or wait, is the above for the configure stuff? then it's ok.
if 'EMCC_STRICT' in os.environ: | ||
shared.Settings.STRICT = os.environ.get('EMCC_STRICT') != '0' | ||
|
||
STRICT = ([None] + filter(lambda x: x.startswith('STRICT='), settings_changes))[-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why [None]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I guess it doesn't matter much, let me go with False
instead since it's evaluated to true/false below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I wasn't clear. My issue isn't with None vs False. It's that I don't understand that line - why does it need [..something..]
at all? I guess it's so the -1
can work, because we happen to just want the last one but the line just seems overcomplicated.
and we do this twice. how about adding a method, get_last_change
which returns the last change, or None if there isn't one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh, I see. Refactored that to its own function, that looks better now, what do you think?
shared.Settings.STRICT = os.environ.get('EMCC_STRICT') != '0' | ||
|
||
STRICT = ([None] + filter(lambda x: x.startswith('STRICT='), settings_changes))[-1] | ||
if STRICT: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worry about creating a var called STRICT
, might confuse us in the future as to what to use. How about temp_strict
or strict_changes
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, opting for strict_cmdline
since the variable comes from a command line setting.
|
||
STRICT = ([None] + filter(lambda x: x.startswith('STRICT='), settings_changes))[-1] | ||
if STRICT: | ||
shared.Settings.STRICT = int(STRICT[len('STRICT='):]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The settings changes would set Settings.STRICT
for you - why do we even need to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, because we need to do the library stuff first.
But then, let's comment that above here and not just below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check.
shared.Settings.ERROR_ON_MISSING_LIBRARIES = 1 | ||
|
||
# Libraries are searched before settings_changes are applied, so pull its value from the command line already here. | ||
ERROR_ON_MISSING_LIBRARIES = ([None] + filter(lambda x: x.startswith('ERROR_ON_MISSING_LIBRARIES='), settings_changes))[-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, why [None]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to False
as well.
3d39bf5
to
4ec5caa
Compare
# Libraries are searched before settings_changes are applied, so apply the value for STRICT and ERROR_ON_MISSING_LIBRARIES from | ||
# command line already now. | ||
|
||
def last_element_or_none(arr): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, but let's share even more code here. this is only used on the settings_changes
array. so you can call this get_last_settings_change
, and receive the name of the pref as a param
lgtm with that. might be some more cleanup worth doing, but we can do it in followups.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh ok, updated.
4ec5caa
to
9b727e8
Compare
This adds a new
EMSCRIPTEN_STRICT
build mode, enabled either with-s EMSCRIPTEN_STRICT=1
or with env. varEMSCRIPTEN_STRICT=1
. That does the following things:#define EMSCRIPTEN
preprocessor value disappears, but code should use#ifdef __EMSCRIPTEN__
.-s ERROR_ON_UNDEFINED_SYMBOLS
defaults to1
instead of the previous default0
. (Default to erroring out link stage if there are any remaining undefined symbols. #2714)-s ERROR_ON_MISSING_LIBRARIES=0/1
, which defaults to 0, but inEMSCRIPTEN_STRICT
mode defaults to 1. (Passing a nonexistent -llibthatisnotthere should be (configurable to be) an error #4213)system/include/emscripten
from the set of system include directories inEMSCRIPTEN_STRICT
mode, with the intent that users should do#include <emscripten/foo.h>
to include Emscripten-related system headers (see also include_emscripten #4664)src/library_xx.js
files, but instead code should use one of the predefined library aliases to link to system libraries, e.g.-lGL
for OpenGL or-lSDL
for SDL 1.x etc., or by using-lfoo.js
to link to them. (Add support for conditionally excluding all handwritten JS code for SDL. #2730)The intent is that these all would become deprecated options, and people would be able to use
EMSCRIPTEN_STRICT
mode to try out ahead of time that their projects build without relying on deprecated features.In this PR, building without
EMSCRIPTEN_STRICT
mode doesn't yet change any existing behavior, but all changes are opt in at this point (although with the wording being about the intent to deprecate)Tried to get as much of the breaking changes in one go to highlight/group the different things that I'd like to change. Note that these are all more or less validation related, where it would be nicest to be as strict as possible by default, like native compilers are.