Skip to content

Commit b4786b6

Browse files
authored
Add bootstrap.py script which takes care of post-checkout tasks (#19736)
This script is only present is git checkout of emscripten and not in emsdk or other packaged versions. When run with no arguments this script ensures that all the post-checkout steps required by emscripten have been run and are up-to-date with the current checkout. It does this by comparing `.stamp` files in the `out` directory with the timestamps of inputs files such as `package.json`. When `emcc` is run, and bootstrap.py is present it is run in `check` mode which will cause the compiler to error if the one or more of the bootstrap commands needs to be run (e.g. if package.json was modified).
1 parent e502920 commit b4786b6

File tree

11 files changed

+242
-7
lines changed

11 files changed

+242
-7
lines changed

.circleci/config.yml

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ commands:
5757
# In order make our external version of emscripten use the emsdk
5858
# config we need to explicitly set EM_CONFIG here.
5959
echo "export EM_CONFIG=~/emsdk/.emscripten" >> $BASH_ENV
60+
bootstrap:
61+
description: "bootstrap"
62+
steps:
63+
- run: ./bootstrap
6064
npm-install:
6165
description: "npm ci"
6266
steps:
@@ -156,6 +160,7 @@ commands:
156160
echo "final .emscripten:"
157161
cat ~/emsdk/.emscripten
158162
- emsdk-env
163+
- bootstrap
159164
- npm-install
160165
build-libs:
161166
description: "Build all libraries"
@@ -214,6 +219,7 @@ commands:
214219
name: submodule update
215220
command: git submodule update --init
216221
- emsdk-env
222+
- bootstrap
217223
- npm-install
218224
- pip-install
219225
upload-test-results:

ChangeLog.md

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ See docs/process.md for more on how version tagging works.
2020

2121
3.1.48 (in development)
2222
-----------------------
23+
- A new top-level `bootstrap` script was added. This script is for emscripten
24+
developers and helps take a care of post-checkout tasks such as `npm install`.
25+
If this script needs to be run (e.g. becuase package.json was changed, emcc
26+
will exit with an error. (#19736)
2327

2428
3.1.47 - 10/09/23
2529
-----------------

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ dist: $(DISTFILE)
77
install:
88
@rm -rf $(DESTDIR)
99
./tools/install.py $(DESTDIR)
10-
npm install --production --prefix $(DESTDIR)
10+
npm install --omit=dev --prefix $(DESTDIR)
1111

1212
# Create an distributable archive of emscripten suitable for use
1313
# by end users. This archive excludes node_modules as it can include native

bootstrap

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/sh
2+
# Copyright 2020 The Emscripten Authors. All rights reserved.
3+
# Emscripten is available under two separate licenses, the MIT license and the
4+
# University of Illinois/NCSA Open Source License. Both these licenses can be
5+
# found in the LICENSE file.
6+
#
7+
# Entry point for running python scripts on UNIX systems.
8+
#
9+
# Automatically generated by `create_entry_points.py`; DO NOT EDIT.
10+
#
11+
# To make modifications to this file, edit `tools/run_python.sh` and then run
12+
# `tools/maint/create_entry_points.py`
13+
14+
# $PYTHON -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal
15+
# of cpython used in cross compilation via setup.py.
16+
unset _PYTHON_SYSCONFIGDATA_NAME
17+
18+
if [ -z "$PYTHON" ]; then
19+
PYTHON=$EMSDK_PYTHON
20+
fi
21+
22+
if [ -z "$PYTHON" ]; then
23+
PYTHON=$(command -v python3 2> /dev/null)
24+
fi
25+
26+
if [ -z "$PYTHON" ]; then
27+
PYTHON=$(command -v python 2> /dev/null)
28+
fi
29+
30+
if [ -z "$PYTHON" ]; then
31+
echo 'unable to find python in $PATH'
32+
exit 1
33+
fi
34+
35+
exec "$PYTHON" -E "$0.py" "$@"

bootstrap.bat

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
:: Entry point for running python scripts on windows systems.
2+
::
3+
:: Automatically generated by `create_entry_points.py`; DO NOT EDIT.
4+
::
5+
:: To make modifications to this file, edit `tools/run_python.bat` and then run
6+
:: `tools/maint/create_entry_points.py`
7+
8+
:: N.b. In Windows .bat scripts, the ':' character cannot appear inside any if () blocks,
9+
:: or there will be a parsing error.
10+
11+
:: All env. vars specified in this file are to be local only to this script.
12+
@setlocal
13+
:: -E will not ignore _PYTHON_SYSCONFIGDATA_NAME an internal
14+
:: of cpython used in cross compilation via setup.py.
15+
@set _PYTHON_SYSCONFIGDATA_NAME=
16+
@set EM_PY=%EMSDK_PYTHON%
17+
@if "%EM_PY%"=="" (
18+
set EM_PY=python
19+
)
20+
21+
:: Work around Windows bug https://github.com/microsoft/terminal/issues/15212 : If this
22+
:: script is invoked via enclosing the invocation in quotes via PATH lookup, then %~f0 and
23+
:: %~dp0 expansions will not work.
24+
:: So first try if %~dp0 might work, and if not, manually look up this script from PATH.
25+
@if exist %~f0 (
26+
set MYDIR=%~dp0
27+
goto FOUND_MYDIR
28+
)
29+
@for %%I in (%~n0.bat) do (
30+
@if exist %%~$PATH:I (
31+
set MYDIR=%%~dp$PATH:I
32+
) else (
33+
echo Fatal Error! Due to a Windows bug, we are unable to locate the path to %~n0.bat.
34+
echo To help this issue, try removing unnecessary quotes in the invocation of emcc,
35+
echo or add Emscripten directory to PATH.
36+
echo See github.com/microsoft/terminal/issues/15212 and
37+
echo github.com/emscripten-core/emscripten/issues/19207 for more details.
38+
)
39+
)
40+
:FOUND_MYDIR
41+
42+
:: Python Windows bug https://bugs.python.org/issue34780: If this script was invoked via a
43+
:: shared stdin handle from the parent process, and that parent process stdin handle is in
44+
:: a certain state, running python.exe might hang here. To work around this, if
45+
:: EM_WORKAROUND_PYTHON_BUG_34780 is defined, invoke python with '< NUL' stdin to avoid
46+
:: sharing the parent's stdin handle to it, avoiding the hang.
47+
48+
:: On Windows 7, the compiler batch scripts are observed to exit with a non-zero errorlevel,
49+
:: even when the python executable above did succeed and quit with errorlevel 0 above.
50+
:: On Windows 8 and newer, this issue has not been observed. It is possible that this
51+
:: issue is related to the above python bug, but this has not been conclusively confirmed,
52+
:: so using a separate env. var EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG to enable the known
53+
:: workaround this issue, which is to explicitly quit the calling process with the previous
54+
:: errorlevel from the above command.
55+
56+
:: Also must use goto to jump to the command dispatch, since we cannot invoke emcc from
57+
:: inside a if() block, because if a cmdline param would contain a char '(' or ')', that
58+
:: would throw off the parsing of the cmdline arg.
59+
@if "%EM_WORKAROUND_PYTHON_BUG_34780%"=="" (
60+
@if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" (
61+
goto NORMAL
62+
) else (
63+
goto NORMAL_EXIT
64+
)
65+
) else (
66+
@if "%EM_WORKAROUND_WIN7_BAD_ERRORLEVEL_BUG%"=="" (
67+
goto MUTE_STDIN
68+
) else (
69+
goto MUTE_STDIN_EXIT
70+
)
71+
)
72+
73+
:NORMAL_EXIT
74+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %*
75+
@exit %ERRORLEVEL%
76+
77+
:MUTE_STDIN
78+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL
79+
@exit /b %ERRORLEVEL%
80+
81+
:MUTE_STDIN_EXIT
82+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %* < NUL
83+
@exit %ERRORLEVEL%
84+
85+
:NORMAL
86+
@"%EM_PY%" -E "%MYDIR%%~n0.py" %*

bootstrap.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python3
2+
"""Bootstrap script for emscripten developers / git users.
3+
4+
After checking out emscripten there are certain steps that need to be
5+
taken before it can be used. This script enumerates and automates
6+
these steps and is able to run just the steps that are needed based
7+
on the timestamps of various input files (kind of like a dumb version
8+
of a Makefile).
9+
"""
10+
import argparse
11+
import os
12+
import shutil
13+
import sys
14+
15+
__rootdir__ = os.path.dirname(os.path.abspath(__file__))
16+
sys.path.insert(0, __rootdir__)
17+
18+
STAMP_DIR = os.path.join(__rootdir__, 'out')
19+
20+
from tools import shared, utils
21+
22+
actions = [
23+
('npm packages', 'package.json', [shutil.which('npm'), 'ci']),
24+
# TODO(sbc): Remove the checked in entry point files and have them
25+
# built on demand by this step.
26+
('create entry points', 'tools/maint/create_entry_points.py', [sys.executable, 'tools/maint/create_entry_points.py']),
27+
('git submodules', 'test/third_party/posixtestsuite/', [shutil.which('git'), 'submodule', 'update', '--init']),
28+
]
29+
30+
31+
def get_stamp_file(action_name):
32+
return os.path.join(STAMP_DIR, action_name.replace(' ', '_') + '.stamp')
33+
34+
35+
def check():
36+
for name, filename, _ in actions:
37+
stamp_file = get_stamp_file(name)
38+
filename = utils.path_from_root(filename)
39+
if not os.path.exists(stamp_file) or os.path.getmtime(filename) > os.path.getmtime(stamp_file):
40+
utils.exit_with_error(f'emscripten setup is not complete ("{name}" is out-of-date). Run bootstrap.py to update')
41+
42+
43+
def main(args):
44+
parser = argparse.ArgumentParser(description=__doc__)
45+
parser.add_argument('-v', '--verbose', action='store_true', help='verbose', default=False)
46+
parser.add_argument('-n', '--dry-run', action='store_true', help='dry run', default=False)
47+
args = parser.parse_args()
48+
49+
for name, filename, cmd in actions:
50+
stamp_file = get_stamp_file(name)
51+
filename = utils.path_from_root(filename)
52+
if os.path.exists(stamp_file) and os.path.getmtime(filename) <= os.path.getmtime(stamp_file):
53+
print('Up-to-date: %s' % name)
54+
continue
55+
print('Out-of-date: %s' % name)
56+
if args.dry_run:
57+
print(' (skipping: dry run) -> %s' % ' '.join(cmd))
58+
return
59+
print(' -> %s' % ' '.join(cmd))
60+
shared.run_process(cmd, cwd=utils.path_from_root())
61+
utils.safe_ensure_dirs(STAMP_DIR)
62+
utils.write_file(stamp_file, 'Timestamp file created by bootstrap.py')
63+
64+
65+
if __name__ == '__main__':
66+
main(sys.argv[1:])

emcc.py

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@
5959

6060
logger = logging.getLogger('emcc')
6161

62+
# In git checkouts of emscripten `bootstrap.py` exists to run post-checkout
63+
# steps. In packaged versions (e.g. emsdk) this file does not exist (because
64+
# it is excluded in tools/install.py) and these steps are assumed to have been
65+
# run already.
66+
if os.path.exists(utils.path_from_root('.git')) and os.path.exists(utils.path_from_root('bootstrap.py')):
67+
import bootstrap
68+
bootstrap.check()
69+
6270
# endings = dot + a suffix, compare against result of shared.suffix()
6371
C_ENDINGS = ['.c', '.i']
6472
CXX_ENDINGS = ['.cppm', '.pcm', '.cpp', '.cxx', '.cc', '.c++', '.CPP', '.CXX', '.C', '.CC', '.C++', '.ii']

site/source/docs/building_from_source/index.rst

+17-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,23 @@
44
Building Emscripten from Source
55
===============================
66

7-
Building Emscripten yourself is an alternative to getting binaries using the emsdk.
8-
9-
Emscripten's core codebase, which is in the main "emscripten" repo, does not need to be compiled (it uses Python for most of the scripting that glues together all the tools). What do need to be compiled are LLVM (which in particular provides clang and wasm-ld) and Binaryen. After compiling them, simply edit the ``.emscripten`` file to point to the right place for each of those tools (if the file doesn't exist yet, run ``emcc`` for the first time).
10-
11-
Get the ``main`` branches, or check the `Packaging <https://github.com/emscripten-core/emscripten/blob/main/docs/packaging.md>`_ instructions to identify precise commits in existing releases.
7+
Building Emscripten yourself is an alternative to getting binaries using the
8+
emsdk.
9+
10+
Emscripten itself is written in Python and JavaScript so it does not need to be
11+
compiled. However, after checkout you will need to perform various steps
12+
before it can be used (e.g. ``npm install``). The ``bootstrap`` script in the
13+
top level of the repository takes care of running these steps and ``emcc`` will
14+
error out if it detects that ``bootstrap`` needs to be run.
15+
16+
In addition to the main emscripten repository you will also need to checkout
17+
and build LLVM and Binaryen (as detailed below). After compiling these, you
18+
will need to edit your ``.emscripten`` file to point to their corresponding
19+
locations.
20+
21+
Use the ``main`` branches of each of these repositories, or check the `Packaging
22+
<https://github.com/emscripten-core/emscripten/blob/main/docs/packaging.md>`_
23+
instructions to identify precise commits used in a specific release.
1224

1325

1426
Building LLVM

test/test_sanity.py

+16
Original file line numberDiff line numberDiff line change
@@ -747,3 +747,19 @@ def test_binaryen_version(self):
747747

748748
make_fake_tool(self.in_dir('fake', 'bin', 'wasm-opt'), '70')
749749
self.check_working([EMCC, test_file('hello_world.c')], 'unexpected binaryen version: 70 (expected ')
750+
751+
def test_bootstrap(self):
752+
restore_and_set_up()
753+
self.run_process([EMCC, test_file('hello_world.c')])
754+
755+
# Touching package.json should cause compiler to fail with bootstrap message
756+
Path(utils.path_from_root('package.json')).touch()
757+
err = self.expect_fail([EMCC, test_file('hello_world.c')])
758+
self.assertContained('emcc: error: emscripten setup is not complete ("npm packages" is out-of-date). Run bootstrap.py to update', err)
759+
760+
# Running bootstrap.py should fix that
761+
bootstrap = shared.bat_suffix(shared.path_from_root('bootstrap'))
762+
self.run_process([bootstrap])
763+
764+
# Now the compiler should work again
765+
self.run_process([EMCC, test_file('hello_world.c')])

tools/install.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919

2020
EXCLUDES = [os.path.normpath(x) for x in '''
2121
test/third_party
22+
tools/maint
2223
site
2324
node_modules
2425
Makefile
2526
.git
2627
cache
2728
cache.lock
29+
bootstrap.py
2830
'''.split()]
2931

3032
EXCLUDE_PATTERNS = '''
@@ -82,7 +84,6 @@ def copy_emscripten(target):
8284

8385

8486
def main():
85-
8687
parser = argparse.ArgumentParser(description=__doc__)
8788
parser.add_argument('-v', '--verbose', action='store_true', help='verbose',
8889
default=int(os.environ.get('EMCC_DEBUG', '0')))

tools/maint/create_entry_points.py

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
'''.split()
2424

2525
entry_points = '''
26+
bootstrap
2627
emar
2728
embuilder
2829
emcmake

0 commit comments

Comments
 (0)