Skip to content

Add options support to contrib ports (contrib ports part 3) #21276

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

Merged
merged 15 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3025,7 +3025,7 @@ def test_sdl2_image_formats(self):
self.btest_exit('test_sdl2_image.c', 600, args=[
'--preload-file', 'screenshot.jpg',
'-DSCREENSHOT_DIRNAME="/"', '-DSCREENSHOT_BASENAME="screenshot.jpg"', '-DBITSPERPIXEL=24', '-DNO_PRELOADED',
'-sUSE_SDL=2', '-sUSE_SDL_IMAGE=2', '-sSDL2_IMAGE_FORMATS=["jpg"]'
'--use-port=sdl2', '--use-port=sdl2_image?formats=jpg'
])

@no_wasm64('SDL2 + wasm64')
Expand Down
2 changes: 1 addition & 1 deletion test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -2372,7 +2372,7 @@ def test_sdl2_ttf(self):
def test_contrib_ports(self):
# Verify that contrib ports can be used (using the only contrib port available ATM, but can be replaced
# with a different contrib port when there is another one
self.emcc(test_file('other/test_contrib_ports.cpp'), ['--use-port=contrib.glfw3'])
self.emcc(test_file('other/test_contrib_ports.cpp'), ['--use-port=contrib.glfw3?disableWarning=true'], output_filename='a.out.js')

def test_link_memcpy(self):
# memcpy can show up *after* optimizations, so after our opportunity to link in libc, so it must be special-cased
Expand Down
19 changes: 18 additions & 1 deletion tools/ports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from tools import system_libs
from tools import utils
from tools.settings import settings
from urllib.parse import parse_qs

from tools.toolchain_profiler import ToolchainProfiler

Expand Down Expand Up @@ -68,6 +69,8 @@ def validate_port(port):
expected_attrs = ['get', 'clear', 'show']
if port.is_contrib:
expected_attrs += ['URL', 'DESCRIPTION', 'LICENSE']
if hasattr(port, 'handle_options'):
expected_attrs += ['OPTIONS']
for a in expected_attrs:
assert hasattr(port, a), 'port %s is missing %s' % (port, a)

Expand Down Expand Up @@ -393,10 +396,24 @@ def add_deps(node):
add_deps(port)


def handle_use_port_arg(settings, name):
def handle_use_port_arg(settings, arg):
args = arg.split('?', 1)
name, options = args[0], args[1] if len(args) == 2 else None
if name not in ports_by_name:
utils.exit_with_error(f'Invalid port name: {name} used with --use-port')
ports_needed.add(name)
if options:
port = ports_by_name[name]
if not hasattr(port, 'handle_options'):
utils.exit_with_error(f'Invalid options for port {name}: No options available')
else:
try:
options_qs = parse_qs(options, strict_parsing=True)
except ValueError as error:
utils.exit_with_error(f'{options} is not valid: {error}. Available options are {port.OPTIONS}.')
if not set(options_qs.keys()).issubset(port.OPTIONS.keys()):
utils.exit_with_error(f'{options} is not valid. Available options are {port.OPTIONS}.')
port.handle_options(options_qs)


def get_needed_ports(settings):
Expand Down
46 changes: 42 additions & 4 deletions tools/ports/contrib/glfw3.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# found in the LICENSE file.

import os
from tools import utils
from typing import Dict

TAG = '1.0.4'
HASH = 'c3c96718e5d2b37df434a46c4a93ddfd9a768330d33f0d6ce2d08c139752894c2421cdd0fefb800fe41fafc2bbe58c8f22b8aa2849dc4fc6dde686037215cfad'
Expand All @@ -13,14 +15,33 @@
DESCRIPTION = 'This project is an emscripten port of GLFW written in C++ for the web/webassembly platform'
LICENSE = 'Apache 2.0 license'

OPTIONS = {
'disableWarning': 'Boolean to disable warnings emitted by the library',
'disableJoystick': 'Boolean to disable support for joystick entirely',
'disableMultiWindow': 'Boolean to disable multi window support which makes the code smaller and faster'
}

# user options (from --use-port)
opts: Dict[str, bool] = {
'disableWarning': False,
'disableJoystick': False,
'disableMultiWindow': False
}


def get_lib_name(settings):
return 'lib_contrib.glfw3.a'
return ('lib_contrib.glfw3' +
('-nw' if opts['disableWarning'] else '') +
('-nj' if opts['disableJoystick'] else '') +
('-sw' if opts['disableMultiWindow'] else '') +
'.a')


def get(ports, settings, shared):
# get the port
ports.fetch_project('contrib.glfw3', f'https://github.com/pongasoft/emscripten-glfw/releases/download/v{TAG}/emscripten-glfw3-{TAG}.zip', sha512hash=HASH)
ports.fetch_project('contrib.glfw3',
f'https://github.com/pongasoft/emscripten-glfw/releases/download/v{TAG}/emscripten-glfw3-{TAG}.zip',
sha512hash=HASH)

def create(final):
root_path = os.path.join(ports.get_dir(), 'contrib.glfw3')
Expand All @@ -29,8 +50,16 @@ def create(final):
for source_include_path in source_include_paths:
ports.install_headers(source_include_path, target='GLFW')

# this should be an option but better to disable for now...
flags = ['-DEMSCRIPTEN_GLFW3_DISABLE_WARNING']
flags = []

if opts['disableWarning']:
flags += ['-DEMSCRIPTEN_GLFW3_DISABLE_WARNING']

if opts['disableJoystick']:
flags += ['-DEMSCRIPTEN_GLFW3_DISABLE_JOYSTICK']

if opts['disableMultiWindow']:
flags += ['-DEMSCRIPTEN_GLFW3_DISABLE_MULTI_WINDOW_SUPPORT']

ports.build_port(source_path, final, 'contrib.glfw3', includes=source_include_paths, flags=flags)

Expand All @@ -52,3 +81,12 @@ def linker_setup(ports, settings):
# includes
def process_args(ports):
return ['-isystem', ports.get_include_dir('contrib.glfw3')]


def handle_options(options):
for option, values in options.items():
value = values[-1] # ignore multiple definitions (last one wins)
if value.lower() in {'true', 'false'}:
opts[option] = value.lower() == 'true'
else:
utils.exit_with_error(f'{option} is expecting a boolean, got {value}')
37 changes: 30 additions & 7 deletions tools/ports/sdl2_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# found in the LICENSE file.

import os
import re
from typing import Dict, Set

TAG = 'release-2.6.0'
HASH = '2175d11a90211871f2289c8d57b31fe830e4b46af7361925c2c30cd521c1c677d2ee244feb682b6d3909cf085129255934751848fc81b480ea410952d990ffe0'
Expand All @@ -14,14 +16,26 @@
'sdl2_image_png': {'SDL2_IMAGE_FORMATS': ["png"]},
}

OPTIONS = {
'formats': 'A comma separated list of formats (ex: png,jpg)'
}

# user options (from --use-port)
opts: Dict[str, Set] = {
'formats': set()
}


def needed(settings):
return settings.USE_SDL_IMAGE == 2


def get_formats(settings):
return set(settings.SDL2_IMAGE_FORMATS).union(opts['formats'])


def get_lib_name(settings):
settings.SDL2_IMAGE_FORMATS.sort()
formats = '-'.join(settings.SDL2_IMAGE_FORMATS)
formats = '-'.join(sorted(get_formats(settings)))

libname = 'libSDL2_image'
if formats != '':
Expand All @@ -44,13 +58,15 @@ def create(final):

defs = ['-O2', '-sUSE_SDL=2', '-Wno-format-security']

for fmt in settings.SDL2_IMAGE_FORMATS:
formats = get_formats(settings)

for fmt in formats:
defs.append('-DLOAD_' + fmt.upper())

if 'png' in settings.SDL2_IMAGE_FORMATS:
if 'png' in formats:
defs += ['-sUSE_LIBPNG']

if 'jpg' in settings.SDL2_IMAGE_FORMATS:
if 'jpg' in formats:
defs += ['-sUSE_LIBJPEG']

ports.build_port(src_dir, final, 'sdl2_image', flags=defs, srcs=srcs)
Expand All @@ -64,13 +80,20 @@ def clear(ports, settings, shared):

def process_dependencies(settings):
settings.USE_SDL = 2
if 'png' in settings.SDL2_IMAGE_FORMATS:
formats = get_formats(settings)
if 'png' in formats:
deps.append('libpng')
settings.USE_LIBPNG = 1
if 'jpg' in settings.SDL2_IMAGE_FORMATS:
if 'jpg' in formats:
deps.append('libjpeg')
settings.USE_LIBJPEG = 1


def handle_options(options):
# options has been parsed from a query string
for fmts in options['formats']:
opts['formats'].update({format.lower() for format in re.findall(r'\b\w+\b', fmts)})


def show():
return 'sdl2_image (-sUSE_SDL_IMAGE=2 or --use-port=sdl2_image; zlib license)'