-
Notifications
You must be signed in to change notification settings - Fork 3.5k
emscripten_strict #4665
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
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. |
emcc.py
Outdated
| 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.
tools/shared.py
Outdated
| 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 |
emcc.py
Outdated
| 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.
tools/shared.py
Outdated
| # 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.
tools/shared.py
Outdated
|
|
||
| # 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 :)
src/library_fs.js
Outdated
| $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.
tools/shared.py
Outdated
|
|
||
| # 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.
tools/shared.py
Outdated
| '-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 |
| 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.
emcc.py
Outdated
| 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?
emcc.py
Outdated
| 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.
emcc.py
Outdated
|
|
||
| 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.
emcc.py
Outdated
| 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
emcc.py
Outdated
| # 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_STRICTbuild mode, enabled either with-s EMSCRIPTEN_STRICT=1or with env. varEMSCRIPTEN_STRICT=1. That does the following things:#define EMSCRIPTENpreprocessor value disappears, but code should use#ifdef __EMSCRIPTEN__.-s ERROR_ON_UNDEFINED_SYMBOLSdefaults to1instead 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_STRICTmode defaults to 1. (Passing a nonexistent -llibthatisnotthere should be (configurable to be) an error #4213)system/include/emscriptenfrom the set of system include directories inEMSCRIPTEN_STRICTmode, 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.jsfiles, but instead code should use one of the predefined library aliases to link to system libraries, e.g.-lGLfor OpenGL or-lSDLfor SDL 1.x etc., or by using-lfoo.jsto 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_STRICTmode to try out ahead of time that their projects build without relying on deprecated features.In this PR, building without
EMSCRIPTEN_STRICTmode 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.