diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d78cb9fb..f7e77c46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,217 +3,229 @@ name: CI on: [push, pull_request] jobs: - ubuntu-latest-html-tests: + Linux: runs-on: ubuntu-latest + container: + image: ${{ matrix.distro == 'alpine' && 'alpine:latest' || matrix.distro == 'ubuntu-20.04' && 'ubuntu:20.04' || 'ubuntu:latest' }} + strategy: + fail-fast: false # Remove this line to fail the whole matrix if one job fails; included for testing as many CI pipelines as possible for now + matrix: + distro: [ubuntu, ubuntu-20.04, alpine] + build-system: [autotools, meson] + compiler: [gcc, clang] + tls: [openssl, mbedtls, none] + exclude: + - distro: alpine + tls: none + - distro: ubuntu-20.04 + tls: none + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + if [ "${{ matrix.distro }}" = "ubuntu" ] || [ "${{ matrix.distro }}" = "ubuntu-20.04" ]; then + apt-get update + export DEBIAN_FRONTEND=noninteractive + apt-get install -y autotools-dev autoconf automake build-essential clang git imagemagick libfltk1.3-dev libmbedtls-dev libpng-dev libssl-dev libturbojpeg0-dev lld make ninja-build pip pkg-config x11-apps x11-utils xvfb zlib1g-dev + # HTML tests require meson 1.4.0 or later + pip3 install meson ${{ matrix.distro == 'ubuntu' && '--break-system-packages' || '' }} + else + apk add build-base autoconf automake clang fltk-dev git libpng-dev libjpeg-turbo-dev lld mbedtls-dev meson ninja-build openssl-dev + fi + + - name: Set git safe directory + run: git config --global --add safe.directory ${GITHUB_WORKSPACE} + + - if: matrix.build-system == 'autotools' + name: Prepare Autotools + run: | + ./autogen.sh + mkdir install build + + # Instead of using a matrix for HTML tests we will only set the option on ubuntu (latest) with gcc, no TLS + - name: Configure Build + run: | + HTML_TESTS_OPT="" + if [ "${{ matrix.distro }}" = "ubuntu" ] && [ "${{matrix.compiler}}" = "gcc" ] && [ "${{ matrix.tls }}" = "none" ]; then + if [ "${{ matrix.build-system }}" = "autotools" ]; then + HTML_TESTS_OPT="--enable-html-tests" + else + HTML_TESTS_OPT="-Dhtml-tests=enabled" + fi + fi + if [ "${{matrix.compiler}}" = "clang" ]; then + export CC=clang CXX=clang++ LDFLAGS="-fuse-ld=lld -Wl,--as-needed" + else + if [ "${{ matrix.distro }}" = "ubuntu" ]; then + export CFLAGS="-flto -Werror=odr -Werror=lto-type-mismatch -Werror=strict-aliasing" + fi + fi + if [ "${{ matrix.build-system }}" = "autotools" ]; then + if [ "${{ matrix.tls }}" = "none" ]; then + export TLS_OPT="--disable-tls" + elif [ "${{ matrix.tls }}" = "mbedtls" ]; then + export TLS_OPT="--disable-openssl" + else + export TLS_OPT="--disable-mbedtls" + fi + else + if [ "${{ matrix.tls }}" = "none" ]; then + export TLS_OPT="-Dtls=disabled -Dopenssl=false -Dmbedtls=false" + elif [ "${{ matrix.tls }}" = "mbedtls" ]; then + export TLS_OPT="-Dmbedtls=true -Dopenssl=false" + else + export TLS_OPT="-Dopenssl=true -Dmbedtls=false" + fi + fi + + if [ "${{ matrix.build-system }}" = "autotools" ]; then + cd build + ../configure --prefix=$(readlink -f ../install) ${TLS_OPT} ${HTML_TESTS_OPT} + else + meson setup build --prefix="/tmp/dillo" ${TLS_OPT} ${HTML_TESTS_OPT} + fi + + - name: Build Dillo for ${{ matrix.distro }} with ${{ matrix.build-system }} and ${{ matrix.compiler }} + run: | + if [ "${{ matrix.build-system }}" = "autotools" ]; then + cd build + make && make install + else + ninja -C build install + fi + + - if: matrix.build-system == 'autotools' + name: (Autotools) Prepare for tests + run: | + mkdir -p ~/.dillo/ + dillorc=$(find install -type f -name dillorc) + cp ${dillorc} ~/.dillo/ + + - name: Run tests + run: | + if [ "${{ matrix.build-system }}" = "autotools" ]; then + cd build + export DILLOBIN=$(readlink -f bin/dillo) + make check || (cat test/html/test-suite.log; false) + export DILLOBIN= + else + meson test -C build -v --print-errorlogs + fi + + - name: Check release fits in a floppy disk of 1.44 MB + run: | + if [ "${{ matrix.build-system }}" = "autotools" ]; then + cd build + make dist-gzip + size=$(stat -c %s dillo-*.tar.gz) + floppy=$((1474560 - 32*1024)) # Leave room for FAT table + echo "Floppy occupation: $(($size * 100 / $floppy)) %" + if [ $size -lt $floppy ]; then + echo 'OK: Fits in floopy disk' + else + echo "FAIL: Release size too big: $size / $floppy" + exit 1 + fi + else + echo "Skipping floppy disk check for Meson build; not yet implemented" + fi + + - name: Run distcheck + run: | + HTML_TESTS_OPT="" + if [ "${{ matrix.distro }}" = "ubuntu" ] && [ "${{matrix.compiler}}" = "gcc" ] && [ "${{ matrix.tls }}" = "none" ]; then + if [ "${{ matrix.build-system }}" = "autotools" ]; then + HTML_TESTS_OPT="--enable-html-tests" + fi + fi + if [ "${{ matrix.distro}}" = "ubuntu" ]; then + if [ "${{ matrix.build-system }}" = "autotools" ]; then + mkdir build-distcheck install-distcheck + cd build-distcheck && ../configure --prefix=$(readlink -f ../install-distcheck) ${HTML_TESTS_OPT} + make distcheck DISTCHECK_CONFIGURE_FLAGS="${HTML_TESTS_OPT}" + else + echo "TODO: run \`meson dist -C build\`; currently errors out" + fi + else + echo "Skipping distcheck for OS ${{ matrix.distro }}" + fi + + macOS-13: + runs-on: macos-13 + strategy: + matrix: + build-system: [autotools, meson] + tls: [openssl-1.1, openssl-3, mbedtls] + steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 with: fetch-depth: 1 + - name: Install dependencies - run: sudo apt install -y libfltk1.3-dev libssl-dev xvfb x11-apps x11-utils imagemagick + run: | + tls_lib="" + if [ "${{ matrix.tls }}" = "mbedtls" ]; then + tls_lib="mbedtls" + elif [ "${{ matrix.tls }}" = "openssl-3" ]; then + brew uninstall openssl@1.1 + tls_lib="openssl@3" + fi + brew install autoconf automake fltk meson ${tls_lib} - - name: autogen + - if: matrix.build-system == 'autotools' + name: Prepare Build run: ./autogen.sh - - name: Make install dir - run: mkdir install build - - name: configure - run: cd build && ../configure --prefix=$(readlink -f ../install) --enable-html-tests - - name: make - run: cd build && make - - name: make install - run: cd build && make install - - name: Copy config to .dillo + + - name: Configure Build run: | - mkdir -p ~/.dillo/ - cp install/etc/dillo/* ~/.dillo/ - - name: make check + if [ "${{ matrix.build-system }}" = "autotools" ]; then + if [ "${{ matrix.tls }}" = "mbedtls" ]; then + ./configure --disable-openssl + else + ./configure --disable-mbedtls + fi + else + if [ "${{ matrix.tls }}" = "mbedtls" ]; then + meson setup build --buildtype=release --prefix=$(pwd)/install -Dmbedtls=true -Dopenssl=false + else + meson setup build --buildtype=release --prefix=$(pwd)/install -Dmbedtls=false -Dopenssl=true + fi + fi + + - name: Build Dillo for macOS-13 with ${{ matrix.build-system }} run: | - export DILLOBIN=$(readlink -f install/bin/dillo) - cd build && make check || (cat test/html/test-suite.log; false) - export DILLOBIN= - - name: Check release fits in a floppy disk of 1.44 MB + if [ "${{ matrix.build-system }}" = "autotools" ]; then + make + else + ninja -C build + fi + + - name: Run tests run: | - cd build - make dist-gzip - size=$(stat -c %s dillo-*.tar.gz) - floppy=$((1474560 - 32*1024)) # Leave room for FAT table - echo "Floppy occupation: $(($size * 100 / $floppy)) %" - if [ $size -lt $floppy ]; then - echo 'OK: Fits in floopy disk' + if [ "${{ matrix.build-system }}" = "autotools" ]; then + make check else - echo "FAIL: Release size too big: $size / $floppy" - exit 1 + # This will give more jobs time to finish while we debug the HTML tests + meson test -C build --timeout-multiplier 2 fi - - name: make distcheck (with HTML tests) + + - name: Run distcheck run: | - export DILLOBIN= - mkdir build-distcheck install-distcheck - cd build-distcheck && ../configure --prefix=$(readlink -f ../install-distcheck) --enable-html-tests - make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-html-tests -# - name: Remove pipes -# run: find test/html -type p -delete || true -# - name: Archive production artifacts -# uses: actions/upload-artifact@v3 -# with: -# name: upload-html-test-results -# path: | -# build/test/html - ubuntu-latest-no-tls: - needs: ubuntu-latest-html-tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - name: Install dependencies - run: sudo apt install -y libfltk1.3-dev - - name: autogen - run: ./autogen.sh - - name: configure - run: ./configure --disable-tls - - name: make - run: make - - name: make check - run: make check - - name: make distcheck - run: make distcheck - ubuntu-latest-mbedtls2: - needs: ubuntu-latest-html-tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - name: Install dependencies - run: sudo apt install -y libfltk1.3-dev libmbedtls-dev - - name: autogen - run: ./autogen.sh - - name: configure - run: ./configure --disable-openssl - - name: make - run: make - - name: make check - run: make check - - name: make distcheck - run: make distcheck - ubuntu-latest-openssl-3: - needs: ubuntu-latest-html-tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - name: Install dependencies - run: sudo apt install -y libfltk1.3-dev libssl-dev - - name: autogen - run: ./autogen.sh - - name: configure - run: ./configure --disable-mbedtls - - name: make - run: make - - name: make check - run: make check - - name: make distcheck - run: make distcheck - ubuntu-latest-with-old-std: - needs: ubuntu-latest-html-tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - name: Install dependencies - run: sudo apt install -y libfltk1.3-dev libssl-dev - - name: autogen - run: ./autogen.sh - - name: configure - run: ./configure --disable-mbedtls CFLAGS="-Werror" CXXFLAGS="-Werror" - - name: make - run: make - - name: make check - run: make check - ubuntu-20-04-openssl-1-1: - needs: ubuntu-latest-html-tests - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - name: Install dependencies - run: sudo apt install -y libfltk1.3-dev libssl-dev - - name: autogen - run: ./autogen.sh - - name: configure - run: ./configure --disable-mbedtls - - name: make - run: make - - name: make check - run: make check - - name: make distcheck - run: make distcheck - alpine-mbedtls-3_6_0: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: jirutka/setup-alpine@v1 - with: - packages: > - build-base - autoconf - automake - fltk-dev - libpng-dev - libjpeg-turbo-dev - mbedtls-dev - - run: | - ./autogen.sh - ./configure - make - make check - shell: alpine.sh {0} - macOS-13-openssl-1-1: - needs: ubuntu-latest-html-tests - runs-on: macos-13 - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - name: Install dependencies - run: brew install autoconf automake fltk - - name: autogen - run: ./autogen.sh - - name: configure - run: ./configure --disable-mbedtls - - name: make - run: make - - name: make check - run: make check - - name: make distcheck - run: make distcheck - macOS-13-openssl-3: - needs: ubuntu-latest-html-tests - runs-on: macos-13 - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - name: Remove old OpenSSL 1.1 - run: brew uninstall openssl@1.1 - - name: Install dependencies - run: brew install autoconf automake fltk openssl@3 - - name: autogen - run: ./autogen.sh - - name: configure - run: ./configure --disable-mbedtls - - name: make - run: make - - name: make check - run: make check - - name: make distcheck - run: make distcheck - freebsd-14-openssl-3: - needs: ubuntu-latest-html-tests + if [ "${{ matrix.build-system }}" = "autotools" ]; then + make distcheck + else + echo "TODO: run \`meson dist -C build\`; currently errors out" + fi + + FreeBSD-14: runs-on: ubuntu-latest + strategy: + matrix: + build-system: [autotools, meson] steps: - uses: actions/checkout@v4 - name: FreeBSD VM build @@ -224,20 +236,31 @@ jobs: usesh: true prepare: | set -x - pkg install -y automake fltk + pkg install -y automake fltk meson ninja run: | set -x pwd freebsd-version - ./autogen.sh - ./configure CPPFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib' - cat config.log - make - make check - ldd src/dillo - windows-mbedtls: - needs: ubuntu-latest-html-tests + if [ "${{ matrix.build-system }}" = "autotools" ]; then + ./autogen.sh + ./configure CPPFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib' + cat config.log + make + make check + ldd src/dillo + else + CPPFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib' meson setup build + ninja -C build + ninja -C build test + ldd build/src/dillo + fi + + Windows: runs-on: windows-latest + strategy: + matrix: + build-system: [autotools, meson] + tls: [openssl, mbedtls] steps: - run: git config --global core.autocrlf input - uses: actions/checkout@v4 @@ -245,15 +268,31 @@ jobs: fetch-depth: 1 - uses: cygwin/cygwin-install-action@master with: - packages: gcc-core gcc-g++ autoconf automake make zlib-devel mbedtls-devel libfltk-devel libiconv-devel libpng-devel libjpeg-devel libgif-devel + packages: autoconf automake gcc-core gcc-g++ libfltk-devel libgif-devel libiconv-devel libjpeg-devel libpng-devel libssl-devel make mbedtls-devel ninja zlib-devel python3-pip - shell: C:\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' run: | set -x cd ${GITHUB_WORKSPACE} pwd ls -l - ./autogen.sh - ./configure - make - make check - ls -l src/dillo + if [ "${{ matrix.build-system }}" = "autotools" ]; then + ./autogen.sh + if [ "${{ matrix.tls }}" = "mbedtls" ]; then + ./configure --disable-openssl + else + ./configure --disable-mbedtls + fi + make + make check + ls -l src/dillo + else + pip3.9 install meson + if [ "${{ matrix.tls }}" = "mbedtls" ]; then + meson setup build -Dmbedtls=true -Dopenssl=false + else + meson setup build -Dmbedtls=false -Dopenssl=true + fi + ninja -C build + ninja -C build test + ls -l build/src/dillo + fi diff --git a/dpi/meson.build b/dpi/meson.build new file mode 100644 index 00000000..ba7b6634 --- /dev/null +++ b/dpi/meson.build @@ -0,0 +1,70 @@ +dpi_link_libs = [dlib, dpip] + +bookmarks_dpi_sources = files( + 'bookmarks.c', + 'dpiutil.c', +) + +cookies_dpi_sources = files( + 'cookies.c', + 'dpiutil.c', +) + +datauri_filter_dpi_sources = files( + 'datauri.c', + 'dpiutil.c', +) + +downloads_dpi_sources = files( + 'downloads.cc', + 'dpiutil.c', +) + +file_dpi_sources = files( + 'dpiutil.c', + 'file.c', +) + +ftp_filter_dpi_sources = files( + 'dpiutil.c', + 'ftp.c', +) + +hello_filter_dpi_sources = files( + 'dpiutil.c', + 'hello.c', +) + +vsource_filter_dpi_sources = files( + 'dpiutil.c', + 'vsource.c', +) + +dpis = { + 'bookmarks': bookmarks_dpi_sources, + 'cookies': cookies_dpi_sources, + 'datauri.filter': datauri_filter_dpi_sources, + 'downloads': downloads_dpi_sources, + 'file': file_dpi_sources, + 'ftp.filter': ftp_filter_dpi_sources, + 'hello.filter': hello_filter_dpi_sources, + 'vsource.filter': vsource_filter_dpi_sources, +} + +# These are all so similar that it's trivial to iterate over +foreach dpi_name, dpi : dpis + installdir = ( + get_option('libdir') / meson.project_name() / 'dpi' / dpi_name.split( + '.', + )[0] + ) + executable( + f'@dpi_name@.dpi', + sources: dpi, + dependencies: fltk, + link_with: dpi_link_libs, + install: true, + install_dir: installdir, + include_directories: incdir, + ) +endforeach diff --git a/dpid/meson.build b/dpid/meson.build new file mode 100644 index 00000000..3801a85a --- /dev/null +++ b/dpid/meson.build @@ -0,0 +1,45 @@ +dpid_sources = files( + 'dpi.c', + 'dpi_socket_dir.c', + 'dpid.c', + 'dpid_common.c', + 'main.c', + 'misc_new.c', +) + +link_libs = [ + dlib, + dpip, +] + +executable( + 'dpid', + sources: dpid_sources, + include_directories: incdir, + link_with: link_libs, + install: true, +) + +executable( + 'dpidc', + sources: 'dpidc.c', + include_directories: incdir, + link_with: link_libs, + install: true, +) + +sed = find_program('sed', required: true) +dpid_libdir = get_option('libdir') # TODO: Can we reuse this? +configure_file( + input: 'dpidrc.in', + output: 'dpidrc', + command: [ + sed, + '-e', f's|[@]libdir[@]|@dpid_libdir@|;s|[@]EXEEXT[@]|@exe_suffix@|g', + '@INPUT@', + ], + capture: true, + install_dir: get_option('sysconfdir'), + install: true, + install_tag: 'runtime', +) diff --git a/dw/meson.build b/dw/meson.build new file mode 100644 index 00000000..7f21f1ea --- /dev/null +++ b/dw/meson.build @@ -0,0 +1,74 @@ +dwcore_sources = files( + 'findtext.cc', + 'imgrenderer.cc', + 'iterator.cc', + 'layout.cc', + 'selection.cc', + 'stackingcontextmgr.cc', + 'style.cc', + 'tools.cc', + 'types.cc', + 'ui.cc', + 'widget.cc', +) + +dw_fltk_sources = files( + 'fltkcomplexbutton.cc', + 'fltkflatview.cc', + 'fltkimgbuf.cc', + 'fltkmisc.cc', + 'fltkplatform.cc', + 'fltkpreview.cc', + 'fltkui.cc', + 'fltkviewbase.cc', + 'fltkviewport.cc', +) + +dw_widgets_sources = files( + 'alignedtablecell.cc', + 'alignedtextblock.cc', + 'bullet.cc', + 'hyphenator.cc', + 'image.cc', + 'listitem.cc', + 'oofawarewidget.cc', + 'oofawarewidget_iterator.cc', + 'ooffloatsmgr.cc', + 'oofposabslikemgr.cc', + 'oofposabsmgr.cc', + 'oofposfixedmgr.cc', + 'oofpositionedmgr.cc', + 'oofposrelmgr.cc', + 'outofflowmgr.cc', + 'regardingborder.cc', + 'ruler.cc', + 'simpletablecell.cc', + 'table.cc', + 'table_iterator.cc', + 'tablecell.cc', + 'textblock.cc', + 'textblock_iterator.cc', + 'textblock_linebreaking.cc', +) + +dwcore = static_library( + 'dwcore', + sources: dwcore_sources, + include_directories: incdir, +) + +dw_fltk = static_library( + 'dw_fltk', + sources: dw_fltk_sources, + dependencies: fltk, + include_directories: incdir, +) + +dw_widgets = static_library( + 'dw_widgets', + sources: dw_widgets_sources, + include_directories: incdir, + link_with: liblout, +) + +dillo_libs += [dwcore, dw_fltk, dw_widgets] diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..76589ec5 --- /dev/null +++ b/meson.build @@ -0,0 +1,397 @@ +project( + 'dillo', + 'cpp', + 'c', + version: '3.1.1', + license: 'GPL-3.0-or-later', + meson_version: '>= 1.4.0', #file.full_path() + default_options: [ + 'c_std=c11', + 'cpp_std=c++11', + 'warning_level=everything', + 'werror=false', + ], +) + +host = host_machine.system() +cxx = meson.get_compiler('cpp') +cc = meson.get_compiler('c') +exe_suffix = host_machine.system() == 'windows' ? 'exe' : '' + +incdir = [include_directories('.')] + +if cxx.get_id() == 'gcc' or cxx.get_id() == 'clang' + add_project_arguments( + '-Wno-unused-parameter', + '-fno-exceptions', + '-D_POSIX_C_SOURCE=200112L', + language: 'c', + ) + add_project_arguments( + '-Wno-unused-parameter', + '-fno-rtti', + '-fno-exceptions', + '-D_POSIX_C_SOURCE=200112L', + language: 'cpp', + ) +endif + +conf = configuration_data() + +# Some basic defines upfront, we'll do the rest right before we configure_file +conf.set_quoted('VERSION', meson.project_version()) +# TODO: This is -DDILLO_BINDIR why? +conf.set_quoted('DILLO_BINDIR', get_option('prefix') / get_option('bindir')) +conf.set_quoted( + 'DILLO_DOCDIR', + get_option('prefix') / get_option('datadir') / 'doc' / meson.project_name(), +) +conf.set_quoted( + 'DILLO_LIBDIR', + get_option('prefix') / get_option('libdir') / meson.project_name(), +) +# This one needs a trailing slash in the dillo source; for 1:1 autotools compat we'll add it here for now +conf.set_quoted( + 'DILLO_SYSCONF', + get_option('prefix') / get_option('sysconfdir') / meson.project_name() + + '/', +) +conf.set_quoted('DPIDRC_SYS', get_option('sysconfdir') / meson.project_name() / 'dpidrc') +conf.set_quoted('EXEEXT', exe_suffix) +conf.set_quoted('PACKAGE', meson.project_name()) + +################### +# Function checks # +################### + +functions = [] + +# 'aix' isn't actually supported by meson, but we'll check for it anyway +if host == 'sunos' or host == 'aix' + functions = ['gethostbyname', 'setsockopt'] + foreach func : functions + if not cc.has_function(func) + error(f'Function @func@ not found') + endif + endforeach +endif + +########### +# Headers # +########### + +# Check for socklen_t (in Unix98) +if host_machine.system() == 'windows' + socket_header = 'ws2tcpip.h' +else + socket_header = 'sys/socket.h' +endif + +if not cc.has_header_symbol(socket_header, 'socklen_t') + code = f''' +#include +#include <@socket_header@> +int accept(int, struct sockaddr *, size_t *); +int main() {} +''' + if cc.compiles(code, name: 'socklen_t is size_t') + conf.set('socklen_t', 'size_t') + else + conf.set('socklen_t', 'int') + endif +endif + +################ +# Dependencies # +################ + +all_deps = [] +dillo_deps = [] + +# FLTK is a bit of a mess; we'll try to use CMake to find it first. +# (Not using 'method: 'cmake' because if pkg-config ever does get implemented we'll want to use that) +# FLTK is painful. Since 2009 they've been 'considering' pkg-config support +# but even though they have most of the pieces in place nobody has bothered to implement +fltk = dependency('FLTK', required: false) # I'm pretty sure cmake falls back to fltk-config anyway. +if not fltk.found() + if meson.is_cross_build() + error('Did not find FLTK using CMake; cross-compilation is not possible') + else + # Fall back to a legacy fltk-config check implemented directly in meson + # Thanks to Eli Schwartz for the following: + fltk_config = find_program('fltk-config') + fltk_include_dirs = run_command( + fltk_config, + '--includedir', + check: true, + ).stdout().strip().split() + fltk_cxxflags = run_command( + fltk_config, + '--cxxflags', + check: true, + ).stdout().strip().split() + #fltk_link_args = run_command(fltk_config, '--ldflags', '--libs', check: + #true).stdout().strip().split() + # --libs _always_ reports the .a file which means if you're using a shared library + # you'll get a link error. We'll just use --ldflags for now. + fltk_link_args = run_command( + fltk_config, + '--ldflags', + check: true, + ).stdout().strip().split() + fltk = declare_dependency( + compile_args: fltk_cxxflags, + include_directories: fltk_include_dirs, + link_args: fltk_link_args, + ) + endif +endif + +# iconv has custom lookup functionality; Meson will check for iconv.h and libiconv +iconv = dependency('iconv', required: true) +jpeg = dependency('libjpeg', required: get_option('jpeg')) +png = dependency('libpng', required: get_option('png')) +pthread = dependency('threads', required: get_option('posix-threads')) +zlib = dependency('zlib', required: true) +x11 = dependency('x11', required: false) # This is not needed on some platforms; we should gate or otherwise handle this + +all_deps = [fltk, iconv, jpeg, png, pthread, zlib, x11] + +use_tls = get_option( + 'tls', +).enable_if(get_option('mbedtls') or get_option('openssl')) +if use_tls.enabled() + if get_option('mbedtls') and get_option('openssl') + error('Cannot enable both mbedtls and openssl') + endif + + conf.set10('ENABLE_TLS', true, description: 'Enable TLS support') + + if get_option('openssl') + openssl = dependency('openssl', required: true) + all_deps += [openssl] + conf.set10('HAVE_OPENSSL', true) + elif get_option('mbedtls') + mbedtls = dependency('mbedtls', required: false) + mbedx509 = dependency('mbedx509', required: false) + mbedcrypto = dependency('mbedcrypto', required: false) + if mbedtls.found() and mbedx509.found() and mbedcrypto.found() + all_deps += [mbedtls, mbedx509, mbedcrypto] + else + # We're probably on an OS with an outdated mbedtls package that doesn't include a pkgconfig (etc). + # Try and find mbedtls3 + mbedtls = cc.find_library('mbedtls', has_headers: 'mbedtls/net.h', required: false) + if mbedtls.found() + # Assume that we can find the other two libraries in the same way; die if we don't + mbedx509 = cc.find_library('mbedx509', required: true) + mbedcrypto = cc.find_library('mbedcrypto', required: true) + all_deps += [mbedtls, mbedx509, mbedcrypto] + else + # If all else fails assume that the user knows what they're doing and this is a very old mbedtls2 + message('Using fallback mbedtls2 definition') + mbedtls = declare_dependency( + include_directories: ['/usr/include/mbedtls2/'], + link_args: [ + '-L/usr/lib/mbedtls2', + '-lmbedtls', + '-lmbedx509', + '-lmbedcrypto', + ], + ) + all_deps += mbedtls + endif + endif + conf.set10('HAVE_MBEDTLS', true) + endif +endif + +### Test Deps: + +efence = dependency('efence', required: get_option('efence')) +gprof = dependency('gprof', required: get_option('gprof')) + +all_deps += [efence, gprof] + +# Rely on meson to die if a required dependency is not found +# Add all 'found' deps to dillo_deps to link against. +foreach dep : all_deps + if dep.found() + dillo_deps += dep + endif +endforeach + +############## +# Misc Logic # +############## + +# Since we tried to keep finding dependencies as tidy as possible we'll explicitly +# do any other checks (and config setting) in one place: here. + +# Legacy autotools would check for old style libiconf and define inbuf_t accordingly; we'll only support new style +conf.set('inbuf_t', 'char') + +if get_option('cookies').disabled() + add_project_arguments('-DDISABLE_COOKIES', language: 'c') + add_project_arguments('-DDISABLE_COOKIES', language: 'cpp') +endif + +if get_option('gif').enabled() + conf.set10('ENABLE_GIF', true, description: 'Enable GIF support') +endif + +if get_option('ipv6').enabled() + # I guess this is C only? + add_project_arguments('-DENABLE_IPV6', language: 'c') +endif + +if jpeg.found() + conf.set10('ENABLE_JPEG', true, description: 'Enable JPEG support') +endif + +if png.found() + conf.set10('ENABLE_PNG', true, description: 'Enable PNG support') +endif + +if get_option('svg').enabled() + conf.set10('ENABLE_SVG', true, description: 'Enable SVG support') +endif + +if get_option('threaded-dns').enabled() + add_project_arguments('-DD_DNS_THREADED', language: 'c') +endif + +if get_option('rtfl').enabled() + add_project_arguments('-DDBG_RTFL', language: 'cpp') +endif + +if get_option('xembed').enabled() + add_project_arguments('-DDISABLE_XEMBED', language: 'cpp') +endif + +# This is a bit more complex; TODO: +# I suspect that those wishing to use insure should set CC and LIBS themselves outside of the build system. +# if test "x$enable_insure" = "xyes" ; then +# CC="insure -Zoi \"compiler $CC\"" +# LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0" +# fi + +# These autotools macros existed previously +# check for these and defines them if they're not present +# TODO: For now we'll just jam @include stdint.h into config.h +# AC_TYPE_INT16_T +# AC_TYPE_UINT16_T +# AC_TYPE_INT32_T +# AC_TYPE_UINT32_T +# We may not need this at all; these types have all been mandatory in posix since 2001! + +conf.set('CA_CERTS_DIR', get_option('ca-certs-dir')) +conf.set('CA_CERTS_FILE', get_option('ca-certs-file')) + +configure_file( + output: 'config.h', + configuration: conf, +) + +install_data( + files('dillorc'), + install_dir: get_option('sysconfdir') / meson.project_name(), + install_tag: 'runtime', +) + +############ +# Subdirs! # +############ + +# We store internal static libs here to link against in the main executable +dillo_libs = [] + +# We could subdir some of these small libs, but honestly it's so small it's not worth it. +dlib = static_library('dlib', sources: files('dlib/dlib.c')) + +liblout_files = files( + 'lout/container.cc', + 'lout/debug.hh', + 'lout/identity.cc', + 'lout/misc.cc', + 'lout/object.cc', + 'lout/signal.cc', + 'lout/unicode.cc', +) + +liblout = static_library( + 'liblout', + sources: liblout_files, + include_directories: incdir, +) + +dpip = static_library( + 'dpip', + sources: files('dpip/dpip.c'), + include_directories: incdir, +) + +dillo_libs += [dlib, dpip, liblout] + +subdir('dpi') +subdir('dw') +subdir('src') +subdir('dpid') +subdir('test') + +################## +# Config Summary # +################## + +summary( + { + 'Build': build_machine.system(), + 'Build CPU Family': build_machine.cpu_family(), + 'Host CPU Family': host_machine.cpu_family(), + 'Cross-compiling': meson.is_cross_build(), + 'Build Endianness': build_machine.endian(), + 'Host Endianness': host_machine.endian(), + 'Target': host, + 'C Compiler': cc.get_id(), + 'C Compiler Version': cc.version(), + 'Linker': cc.get_linker_id(), + }, + section: 'Environment', +) + +featurevals = { + 'TLS': use_tls.enabled(), +} + +if use_tls.enabled() + if get_option('openssl') + featurevals += {'OpenSSL': openssl} + elif get_option('mbedtls') + featurevals += {'Mbed TLS': mbedtls} + endif +endif + +featurevals += { + 'Cookies': get_option('cookies').enabled(), + 'GIF': get_option('gif').enabled(), + 'JPEG': jpeg.found() ? jpeg : false, + 'PNG': png.found() ? png : false, + 'SVG': get_option('svg').enabled(), + 'XEmbed': get_option('xembed').enabled(), +} + +summary( + featurevals, + bool_yn: true, + section: 'Features', +) + +summary( + { + 'Electric Fence': efence.found() ? efence : false, + 'gprof': gprof.found() ? gprof : false, + 'RTFL': get_option('rtfl').enabled(), + 'HTML tests': get_option('html-tests').enabled(), + }, + bool_yn: true, + section: 'Test and Debugging', +) diff --git a/meson.options b/meson.options new file mode 100644 index 00000000..e8f32d54 --- /dev/null +++ b/meson.options @@ -0,0 +1,84 @@ +option( + 'ca-certs-dir', + type: 'string', + description: 'Specify where to find a directory containing trusted CA certificates for TLS', +) +option( + 'ca-certs-file', + type: 'string', + description: 'Specify where to find a bundle of trusted CA certificates for TLS', +) +option( + 'cookies', + type: 'feature', + value: 'enabled', + description: 'Enable cookie support', +) +option('gif', type: 'feature', value: 'auto', description: 'Enable gif support') +option('ipv6', type: 'feature', value: 'enabled', description: 'IPv6 support') # There is rarely a reason to disable this +option( + 'jpeg', + type: 'feature', + value: 'auto', + description: 'Enable jpeg support', +) +option('png', type: 'feature', value: 'auto', description: 'Enable png support') +option( + 'posix-threads', + type: 'feature', + value: 'auto', + description: 'Enable POSIX thread support', +) +option('svg', type: 'feature', value: 'auto', description: 'Enable svg support') +option( + 'threaded-dns', + type: 'feature', + value: 'enabled', + description: 'Enable threaded DNS support', +) +option( + 'tls', + type: 'feature', + value: 'enabled', + description: 'Enable TLS support', +) # Default to enabling tls +option('mbedtls', type: 'boolean', value: false, description: 'TLS via mbedTLS') +option('openssl', type: 'boolean', value: true, description: 'TLS via OpenSSL') +option( + 'xembed', + type: 'feature', + value: 'enabled', + description: 'Enable XEmbed support', +) + +# Testing, safety, and debugging options +option( + 'efence', + type: 'feature', + value: 'disabled', + description: 'Enable Electric Fence support', +) +option( + 'gprof', + type: 'feature', + value: 'disabled', + description: 'Enable gprof support', +) +option( + 'html-tests', + type: 'feature', + value: 'disabled', + description: 'Enable HTML tests (requires xvfb, xwd and imagemagick)', +) +# option( +# 'insure', +# type: 'boolean', +# value: false, +# description: 'Enable Insure++ support', +# ) +option( + 'rtfl', + type: 'feature', + value: 'disabled', + description: 'Print low-level RTFL messages for debugging the renderer (very large slowdown)', +) diff --git a/src/IO/meson.build b/src/IO/meson.build new file mode 100644 index 00000000..cc579abe --- /dev/null +++ b/src/IO/meson.build @@ -0,0 +1,29 @@ +libdio_sources = files( + 'IO.c', + 'Url.h', + 'about.c', + 'dpi.c', + 'http.c', + 'iowatch.cc', + 'mime.c', + 'tls.c', +) + +# TODO: What's supposed to happen here if tls isn't enabled? What does tls.c do then? +if get_option('tls').enabled() + if get_option('mbedtls') + libdio_sources += files('tls_mbedtls.c') + elif get_option('openssl') + libdio_sources += files('tls_openssl.c') + endif +endif + +libdio = static_library( + 'libdio', + sources: libdio_sources, + dependencies: fltk, + include_directories: incdir, + link_with: dpip, +) + +dillo_libs += libdio diff --git a/src/image.hh b/src/image.hh index 2dc87ecc..6b7b3bfc 100644 --- a/src/image.hh +++ b/src/image.hh @@ -22,7 +22,7 @@ extern "C" { #endif /* __cplusplus */ - +#include /* for int32_t */ #include "bitvec.h" #include "url.h" @@ -99,4 +99,3 @@ void a_Image_abort(DilloImage *Image); #endif /* __cplusplus */ #endif /* __IMAGE_HH__ */ - diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..60eddc51 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,75 @@ +subdir('IO') + +dillo_files = files( + 'auth.c', + 'bitvec.c', + 'bookmark.c', + 'bw.c', + 'cache.c', + 'capi.c', + 'chain.c', + 'colors.c', + 'cookies.c', + 'css.cc', + 'cssparser.cc', + 'decode.c', + 'dialog.cc', + 'dicache.c', + 'digest.c', + 'dillo.cc', + 'dns.c', + 'domain.c', + 'dpiapi.c', + 'findbar.cc', + 'form.cc', + 'gif.c', + 'history.c', + 'hsts.c', + 'html.cc', + 'image.cc', + 'imgbuf.cc', + 'jpeg.c', + 'keys.cc', + 'klist.c', + 'md5.c', + 'menu.cc', + 'misc.c', + 'nav.c', + 'paths.cc', + 'plain.cc', + 'png.c', + 'prefs.c', + 'prefsparser.cc', + 'styleengine.cc', + 'svg.c', + 'table.cc', + 'timeout.cc', + 'tipwin.cc', + 'ui.cc', + 'uicmd.cc', + 'url.c', + 'utf8.cc', + 'web.cc', + 'xembed.cc', +) + +dillo = executable( + 'dillo', + sources: dillo_files, + dependencies: dillo_deps, + include_directories: incdir, + install: true, + link_with: dillo_libs, +) + +sysconf_files = files( + 'domainrc', + 'hsts_preload', + 'keysrc', +) + +install_data( + sysconf_files, + install_dir: get_option('sysconfdir') / meson.project_name(), + install_tag: 'runtime', +) diff --git a/src/prefs.h b/src/prefs.h index 5f8f393d..ec58b5a3 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -13,6 +13,8 @@ #ifndef __PREFS_H__ #define __PREFS_H__ +#include + #include "url.h" #ifdef __cplusplus diff --git a/src/uicmd.hh b/src/uicmd.hh index f75d9a48..3e5682df 100644 --- a/src/uicmd.hh +++ b/src/uicmd.hh @@ -13,6 +13,8 @@ #ifndef __UICMD_HH__ #define __UICMD_HH__ +/* This is C++ and we should be using but this file is currently included in a C file so... */ +#include #include "bw.h" #ifdef __cplusplus diff --git a/src/web.hh b/src/web.hh index c9a8fe10..74829219 100644 --- a/src/web.hh +++ b/src/web.hh @@ -2,6 +2,7 @@ #define __WEB_H__ #include /* for FILE */ +#include /* for int32_t */ #include "bw.h" /* for BrowserWindow */ #include "cache.h" /* for CA_Callback_t */ #include "image.hh" /* for DilloImage */ diff --git a/src/xembed.hh b/src/xembed.hh index d7663c8e..8fc9271d 100644 --- a/src/xembed.hh +++ b/src/xembed.hh @@ -3,6 +3,8 @@ #include +#include + #include "d_size.h" class Xembed : public Fl_Window { diff --git a/test/html/driver.sh b/test/html/driver.sh index 08bdb13e..0bddb318 100755 --- a/test/html/driver.sh +++ b/test/html/driver.sh @@ -19,6 +19,11 @@ if [ ! -e $DILLOBIN ]; then exit 1 fi +magick_bin="convert" +if command -v magick 2>&1 >/dev/null; then + magick_bin="magick" +fi + function render_page() { htmlfile="$1" outpic="$2" @@ -35,7 +40,7 @@ function render_page() { echo "cannot find Dillo window" >&2 exit 1 fi - xwd -id "$winid" -silent | convert xwd:- png:${outpic} + xwd -id "$winid" -silent | ${magick_bin} xwd:- png:${outpic} kill "$dillopid" } diff --git a/test/meson.build b/test/meson.build new file mode 100644 index 00000000..3ec82a5e --- /dev/null +++ b/test/meson.build @@ -0,0 +1,279 @@ +# Unit tests + +unittestdir = meson.project_source_root() / 'test' / 'unit' + +containers = executable( + 'containers', + 'unit/containers.cc', + link_with: [liblout, dlib], + include_directories: incdir, +) +cookies = executable( + 'cookies', + 'unit/cookies.c', + link_with: [dpip, dlib], + include_directories: incdir, +) +identity = executable( + 'identity', + 'unit/identity.cc', + link_with: [liblout, dlib], + include_directories: incdir, +) +liang = executable( + 'liang', + 'unit/liang.cc', + dependencies: [fltk, x11], + link_with: [dlib, liblout, dwcore, dw_fltk, dw_widgets], + include_directories: incdir, + cpp_args: f'-DCUR_SRC_DIR="@unittestdir@"', +) +notsosimplevector = executable( + 'notsosimplevector', + 'unit/notsosimplevector.cc', + include_directories: incdir, +) +shapes = executable( + 'shapes', + 'unit/shapes.cc', + link_with: [liblout, dlib, dwcore], + include_directories: incdir, +) +trie = executable( + 'trie', + 'unit/trie.cc', + dependencies: [fltk, x11], + link_with: [dlib, liblout, dwcore, dw_fltk, dw_widgets], + include_directories: incdir, +) +unicode_test = executable( + 'unicode_test', + 'unit/unicode_test.cc', + dependencies: [fltk, x11], + link_with: liblout, + include_directories: incdir, +) + +test('containers', containers) +test('cookies', cookies, should_fail: true) +test('identity', identity) +test('liang', liang) +test('notsosimplevector', notsosimplevector) +test('shapes', shapes) +test('trie', trie, should_fail: true) + +# dw +# According to the old Makefile.am, most of these tests are supposed to be run manually as they require +# user interaction. + +dw_test_deps = [fltk, x11] +dw_link_with = [dlib, liblout, dwcore, dw_fltk, dw_widgets] + +dw_anchors_test = executable( + 'dw_anchors_test', + 'dw/dw_anchors_test.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_border_test = executable( + 'dw_border_test', + 'dw/dw_border_test.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_example = executable( + 'dw_example', + 'dw/dw_example.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_find_test = executable( + 'dw_find_test', + 'dw/dw_find_test.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_float_test = executable( + 'dw_float_test', + 'dw/dw_float_test.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_image_background = executable( + 'dw_image_background', + 'dw/dw_image_background.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_images_scaled = executable( + 'dw_images_scaled', + 'dw/dw_images_scaled.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_images_scaled2 = executable( + 'dw_images_scaled2', + 'dw/dw_images_scaled2.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_images_simple = executable( + 'dw_images_simple', + 'dw/dw_images_simple.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_imgbuf_mem_test = executable( + 'dw_imgbuf_mem_test', + 'dw/dw_imgbuf_mem_test.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_links = executable( + 'dw_links', + 'dw/dw_links.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_links2 = executable( + 'dw_links2', + 'dw/dw_links2.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_lists = executable( + 'dw_lists', + 'dw/dw_lists.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_resource_test = executable( + 'dw_resource_test', + 'dw/dw_resource_test.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_simple_container_sources = files('dw/dw_simple_container.cc', 'dw/dw_simple_container_test.cc') +dw_simple_container_test = executable( + 'dw_simple_container_test', + dw_simple_container_sources, + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_table = executable( + 'dw_table', + 'dw/dw_table.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_table_aligned = executable( + 'dw_table_aligned', + 'dw/dw_table_aligned.cc', + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) +dw_ui_test_sources = files('dw/dw_ui_test.cc', 'dw/form.cc') +dw_ui_test = executable( + 'dw_ui_test', + dw_ui_test_sources, + dependencies: dw_test_deps, + link_with: dw_link_with, + include_directories: incdir, +) + +test('DW imgbuf mem test', dw_imgbuf_mem_test) + +if get_option('html-tests').enabled() + imgformats = ['png', 'svg'] + foreach imgformat : imgformats + if not get_option(imgformat).enabled() + error(f'Option \'@imgformat@\' is required for the HTML tests') + endif + endforeach + html_tests = [ + 'html/render/b-div.html', + 'html/render/div-100-percent-with-padding.html', + 'html/render/float-img-justify.html', + 'html/render/github-infinite-loop.html', + 'html/render/hackernews.html', + 'html/render/img-aspect-ratio.html', + 'html/render/main-style.html', + 'html/render/margin-auto.html', + 'html/render/max-width-body.html', + 'html/render/max-width-div.html', + 'html/render/max-width-div-clamp.html', + 'html/render/max-width-html.html', + 'html/render/max-width-nested-div.html', + 'html/render/meta-refresh-0-no-url.html', + 'html/render/min-width-body.html', + 'html/render/min-width-div-extend.html', + 'html/render/min-width-div.html', + 'html/render/min-width-html.html', + 'html/render/min-width-nested-div.html', + 'html/render/span-padding.html', + 'html/render/svg-current-color.html', + 'html/render/table-max-width.html', + 'html/render/table-missing-width-in-one-column.html', + 'html/render/table-td-width-percent-img.html', + 'html/render/table-td-width-percent.html', + 'html/render/table-thead-tfoot.html', + 'html/render/table-thead-tfoot-open-tag.html', + 'html/render/white-space.html', + ] + + # To be fixed + xfail_tests = [ + 'html/render/div-100-percent-with-padding.html', + 'html/render/float-img-justify.html', + 'html/render/img-aspect-ratio.html', + 'html/render/margin-auto.html', + 'html/render/max-width-html.html', + 'html/render/min-width-html.html', + 'html/render/span-padding.html', + 'html/render/table-td-width-percent.html', + ] + + dillo_path = dillo.full_path() + + foreach file : html_tests + test_name = file.substring(12, -5) + test_file = files(file) + test( + test_name, + files('html/driver.sh'), + env: [f'DILLOBIN=@dillo_path@'], + args: test_file[0].full_path(), + is_parallel: false, + ) + endforeach + + foreach file : xfail_tests + test_name = file.substring(12, -5) + test_file = files(file) + test( + test_name, + files('html/driver.sh'), + env: [f'DILLOBIN=@dillo_path@'], + args: test_file[0].full_path(), + should_fail: true, + is_parallel: false, + ) + endforeach +endif