Skip to content

Commit 1e79d01

Browse files
authored
Add contrib ports mechanism (#21244)
1 parent 50d2d32 commit 1e79d01

File tree

13 files changed

+186
-19
lines changed

13 files changed

+186
-19
lines changed

ChangeLog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ See docs/process.md for more on how version tagging works.
3030
the top of the JS file. This is useful as it allows things like `{{{
3131
POINTER_SIZE }}}` and `{{{ makeGetValue(..) }}}` to be used in pre/post JS
3232
files, just like they can be in JS library files. (#21227)
33+
- Added concept of contrib ports which are ports contributed by the wider
34+
community and supported on a "best effort" basis. A first contrib port is
35+
available via `--use-port=contrib.glfw3`: an emscripten port of glfw written
36+
in C++ with many features like support for multiple windows. (#21244)
37+
3338

3439
3.1.53 - 01/29/24
3540
-----------------

embuilder.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ def get_help():
126126
return '''
127127
Available targets:
128128
129-
build / clear %s
129+
build / clear
130+
%s
130131
131132
Issuing 'embuilder build ALL' causes each task to be built.
132133
''' % '\n '.join(all_tasks)

site/source/docs/compiling/Building-Projects.rst

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,21 @@ To see a list of all available ports, run ``emcc --show-ports``.
241241
.. note:: Since emscripten 3.1.54, ``--use-port`` is the preferred syntax to use a port in your project. The legacy syntax (for example ``-sUSE_SDL2``, ``-sUSE_SDL_IMAGE=2``) remains available.
242242

243243

244+
Contrib ports
245+
-------------
246+
247+
Contrib ports are contributed by the wider community and supported on a
248+
"best effort" basis. Since they are not run as part of emscripten CI they are
249+
not always guaranteed to build or function.
250+
See :ref:`Contrib Ports <Contrib-Ports>` for more information.
251+
244252
Adding more ports
245253
-----------------
246254

247-
Adding more ports is fairly easy. Basically, the steps are
255+
The simplest way to add a new port is to put it under the ``contrib`` directory. Basically, the steps are:
248256

249257
* Make sure the port is open source and has a suitable license.
250-
* Add it to emscripten-ports on github. The ports maintainers can create the repo and add the relevant developers to a team for that repo, so they have write access.
251-
* Add a script to handle it under ``tools/ports/`` (see existing code for examples) and use it in ``tools/ports/__init__.py``.
252-
* Add testing in the test suite.
258+
* Read the ``README.md`` file under ``tools/ports/contrib`` which contains more information.
253259

254260

255261
Build system issues
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.. _Contrib-Ports:
2+
3+
========================
4+
Emscripten Contrib Ports
5+
========================
6+
7+
Contrib ports are contributed by the wider community and
8+
supported on a "best effort" basis. Since they are not run as part
9+
of emscripten CI they are not always guaranteed to build or function.
10+
11+
The following is the complete list of the contrib ports that are
12+
available in emscripten. In order to use a contrib port you use the
13+
``--use-port=<port_name>`` option (:ref:`emcc <emcc-use-port>`).
14+
15+
.. _contrib.glfw3:
16+
17+
contrib.glfw3
18+
=============
19+
20+
This project is an emscripten port of glfw written in C++ for the web/webassembly platform
21+
22+
`Project information <https://github.com/pongasoft/emscripten-glfw>`_
23+
24+
License: Apache 2.0 license

site/source/docs/compiling/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This section contains topics about building projects and running the output.
1111
- :ref:`Running-html-files-with-emrun` explains how to use *emrun* to run generated HTML pages in a locally launched web server.
1212
- :ref:`Deploying-Pages` covers topics related to hosting Emscripten compiled web pages on a CDN.
1313
- :ref:`GitLab` explains how to build and test projects on GitLab.
14+
- :ref:`Contrib-Ports` contains information about contrib ports.
1415

1516

1617
.. toctree::
@@ -22,3 +23,4 @@ This section contains topics about building projects and running the output.
2223
Running-html-files-with-emrun
2324
Deploying-Pages
2425
GitLab
26+
Contrib-Ports

test/other/test_contrib_ports.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2024 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+
8+
#include <GLFW/glfw3.h>
9+
#include <GLFW/emscripten_glfw3.h>
10+
#include <assert.h>
11+
12+
// cpp otherwise it fails to link
13+
int main() {
14+
15+
assert(glfwInit() == GLFW_TRUE);
16+
17+
GLFWwindow* window = glfwCreateWindow(320, 200, "test_glfw3_port", 0, 0);
18+
assert(window != 0);
19+
// this call ensures that it uses the right port
20+
assert(emscripten_glfw_is_window_fullscreen(window) == EM_FALSE);
21+
glfwTerminate();
22+
23+
24+
return 0;
25+
}

test/test_other.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,6 +2369,11 @@ def test_sdl2_ttf(self):
23692369
self.emcc(test_file('browser/test_sdl2_ttf.c'), args=['-sUSE_SDL=2', '-sUSE_SDL_TTF=2'], output_filename='a.out.js')
23702370
self.emcc(test_file('browser/test_sdl2_ttf.c'), args=['--use-port=sdl2', '--use-port=sdl2_ttf'], output_filename='a.out.js')
23712371

2372+
def test_contrib_ports(self):
2373+
# Verify that contrib ports can be used (using the only contrib port available ATM, but can be replaced
2374+
# with a different contrib port when there is another one
2375+
self.emcc(test_file('other/test_contrib_ports.cpp'), ['--use-port=contrib.glfw3'])
2376+
23722377
def test_link_memcpy(self):
23732378
# memcpy can show up *after* optimizations, so after our opportunity to link in libc, so it must be special-cased
23742379
create_file('main.c', r'''

test/test_sanity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ def test_emcc_ports(self):
529529

530530
# listing ports
531531
out = self.do([EMCC, '--show-ports'])
532-
self.assertContained('Available ports:', out)
532+
self.assertContained('Available official ports:', out)
533533
self.assertContained('sdl2', out)
534534
self.assertContained('sdl2_image', out)
535535
self.assertContained('sdl2_net', out)

tools/link.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2941,6 +2941,8 @@ def run(linker_inputs, options, state, newargs):
29412941
logger.debug('stopping after linking to object file')
29422942
return 0
29432943

2944+
phase_calculate_system_libraries(state, linker_arguments, newargs)
2945+
29442946
js_syms = {}
29452947
if (not settings.SIDE_MODULE or settings.ASYNCIFY) and not shared.SKIP_SUBPROCS:
29462948
js_info = get_js_sym_info()
@@ -2963,8 +2965,6 @@ def add_js_deps(sym):
29632965
settings.ASYNCIFY_IMPORTS_EXCEPT_JS_LIBS = settings.ASYNCIFY_IMPORTS[:]
29642966
settings.ASYNCIFY_IMPORTS += ['*.' + x for x in js_info['asyncFuncs']]
29652967

2966-
phase_calculate_system_libraries(state, linker_arguments, newargs)
2967-
29682968
phase_link(linker_arguments, wasm_target, js_syms)
29692969

29702970
# Special handling for when the user passed '-Wl,--version'. In this case the linker

tools/ports/__init__.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@
3434

3535

3636
def load_port(name):
37-
expected_attrs = ['get', 'clear', 'show']
38-
port = __import__(name, globals(), level=1)
37+
port = __import__(name, globals(), level=1, fromlist=[None])
3938
ports.append(port)
39+
port.is_contrib = name.startswith('contrib.')
4040
port.name = name
4141
ports_by_name[port.name] = port
42-
for a in expected_attrs:
43-
assert hasattr(port, a), 'port %s is missing %s' % (port, a)
4442
if not hasattr(port, 'needed'):
4543
port.needed = lambda s: name in ports_needed
4644
else:
@@ -57,13 +55,31 @@ def load_port(name):
5755
if not hasattr(port, 'variants'):
5856
# port variants (default: no variants)
5957
port.variants = {}
58+
if not hasattr(port, 'show'):
59+
port.show = lambda: f'{port.name} (--use-port={port.name}; {port.LICENSE})'
6060

6161
for variant, extra_settings in port.variants.items():
6262
if variant in port_variants:
6363
utils.exit_with_error('duplicate port variant: %s' % variant)
6464
port_variants[variant] = (port.name, extra_settings)
6565

6666

67+
def validate_port(port):
68+
expected_attrs = ['get', 'clear', 'show']
69+
if port.is_contrib:
70+
expected_attrs += ['URL', 'DESCRIPTION', 'LICENSE']
71+
for a in expected_attrs:
72+
assert hasattr(port, a), 'port %s is missing %s' % (port, a)
73+
74+
75+
def validate_ports():
76+
for port in ports:
77+
validate_port(port)
78+
for dep in port.deps:
79+
if dep not in ports_by_name:
80+
utils.exit_with_error('unknown dependency in port: %s' % dep)
81+
82+
6783
@ToolchainProfiler.profile()
6884
def read_ports():
6985
for filename in os.listdir(ports_dir):
@@ -72,10 +88,14 @@ def read_ports():
7288
filename = os.path.splitext(filename)[0]
7389
load_port(filename)
7490

75-
for port in ports:
76-
for dep in port.deps:
77-
if dep not in ports_by_name:
78-
utils.exit_with_error('unknown dependency in port: %s' % dep)
91+
contrib_dir = os.path.join(ports_dir, 'contrib')
92+
for filename in os.listdir(contrib_dir):
93+
if not filename.endswith('.py') or filename == '__init__.py':
94+
continue
95+
filename = os.path.splitext(filename)[0]
96+
load_port('contrib.' + filename)
97+
98+
validate_ports()
7999

80100

81101
def get_all_files_under(dirname):
@@ -441,9 +461,15 @@ def add_cflags(args, settings): # noqa: U100
441461

442462

443463
def show_ports():
444-
print('Available ports:')
445-
for port in sorted(ports, key=lambda p: p.name):
446-
print(' ', port.show())
464+
sorted_ports = sorted(ports, key=lambda p: p.name)
465+
print('Available official ports:')
466+
for port in sorted_ports:
467+
if not port.is_contrib:
468+
print(' ', port.show())
469+
print('Available contrib ports:')
470+
for port in sorted_ports:
471+
if port.is_contrib:
472+
print(' ', port.show())
447473

448474

449475
read_ports()

tools/ports/contrib/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Emscripten "Contrib" Ports
2+
==========================
3+
4+
Ports in this directory are contributed by the wider community and are
5+
supported on a "best effort" basis. Since they are not run as part of
6+
emscripten CI they are not always guaranteed to build or function.
7+
8+
If you want to add a contrib port, please use another contrib port as
9+
an example. In particular, each contrib port must provide 3 extra piece
10+
of information:
11+
12+
* `URL`: the url where the user can find more information about
13+
the project/port
14+
* `DESCRIPTION`: a (short) description of what the project/port
15+
is about
16+
* `LICENSE`: the license used by the project/port
17+
18+
After adding a contrib port, you should consider modifying the documentation
19+
under `site/source/docs/compiling/Contrib-Ports.rst`.

tools/ports/contrib/__init__.py

Whitespace-only changes.

tools/ports/contrib/glfw3.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright 2024 The Emscripten Authors. All rights reserved.
2+
# Emscripten is available under two separate licenses, the MIT license and the
3+
# University of Illinois/NCSA Open Source License. Both these licenses can be
4+
# found in the LICENSE file.
5+
6+
import os
7+
8+
TAG = '1.0.4'
9+
HASH = 'c3c96718e5d2b37df434a46c4a93ddfd9a768330d33f0d6ce2d08c139752894c2421cdd0fefb800fe41fafc2bbe58c8f22b8aa2849dc4fc6dde686037215cfad'
10+
11+
# contrib port information (required)
12+
URL = 'https://github.com/pongasoft/emscripten-glfw'
13+
DESCRIPTION = 'This project is an emscripten port of glfw written in C++ for the web/webassembly platform'
14+
LICENSE = 'Apache 2.0 license'
15+
16+
17+
def get_lib_name(settings):
18+
return 'lib_contrib.glfw3.a'
19+
20+
21+
def get(ports, settings, shared):
22+
# get the port
23+
ports.fetch_project('contrib.glfw3', f'https://github.com/pongasoft/emscripten-glfw/releases/download/v{TAG}/emscripten-glfw3-{TAG}.zip', sha512hash=HASH)
24+
25+
def create(final):
26+
root_path = os.path.join(ports.get_dir(), 'contrib.glfw3')
27+
source_path = os.path.join(root_path, 'src', 'cpp')
28+
source_include_paths = [os.path.join(root_path, 'external', 'GLFW'), os.path.join(root_path, 'include', 'GLFW')]
29+
for source_include_path in source_include_paths:
30+
ports.install_headers(source_include_path, target='GLFW')
31+
32+
# this should be an option but better to disable for now...
33+
flags = ['-DEMSCRIPTEN_GLFW3_DISABLE_WARNING']
34+
35+
ports.build_port(source_path, final, 'contrib.glfw3', includes=source_include_paths, flags=flags)
36+
37+
return [shared.cache.get_lib(get_lib_name(settings), create, what='port')]
38+
39+
40+
def clear(ports, settings, shared):
41+
shared.cache.erase_lib(get_lib_name(settings))
42+
43+
44+
def linker_setup(ports, settings):
45+
root_path = os.path.join(ports.get_dir(), 'contrib.glfw3')
46+
source_js_path = os.path.join(root_path, 'src', 'js', 'lib_emscripten_glfw3.js')
47+
settings.JS_LIBRARIES += [source_js_path]
48+
49+
50+
# Using contrib.glfw3 to avoid installing headers into top level include path
51+
# so that we don't conflict with the builtin GLFW headers that emscripten
52+
# includes
53+
def process_args(ports):
54+
return ['-isystem', ports.get_include_dir('contrib.glfw3')]

0 commit comments

Comments
 (0)