From 594768dc76a80ae934db1a6c78e9c7ef9034c931 Mon Sep 17 00:00:00 2001 From: Mohammad Nejati Date: Tue, 10 Feb 2026 12:58:32 +0000 Subject: [PATCH] add brotli.jam --- src/tools/brotli.jam | 155 +++++++++++++++++++++++++++++++++++++++++++ test/libbrotli.py | 120 +++++++++++++++++++++++++++++++++ test/test_all.py | 1 + 3 files changed, 276 insertions(+) create mode 100644 src/tools/brotli.jam create mode 100644 test/libbrotli.py diff --git a/src/tools/brotli.jam b/src/tools/brotli.jam new file mode 100644 index 0000000000..83f78d4cf9 --- /dev/null +++ b/src/tools/brotli.jam @@ -0,0 +1,155 @@ +# Copyright (c) 2025 Mohammad Nejati +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE.txt or +# https://www.bfgroup.xyz/b2/LICENSE.txt) + +# Supports the brotli library +# +# After 'using brotli', the following targets are available: +# +# /brotli//brotlicommon -- The brotli common library +# /brotli//brotlidec -- The brotli decoder library +# /brotli//brotlienc -- The brotli encoder library + +import project ; +import ac ; +import errors ; +import feature ; +import "class" : new ; +import targets ; +import path ; +import modules ; +import indirect ; +import property ; +import property-set ; +import args ; + +header = brotli/decode.h ; +brotlicommon_names = brotlicommon libbrotlicommon ; +brotlidec_names = brotlidec libbrotlidec ; +brotlienc_names = brotlienc libbrotlienc ; + +library-id = 0 ; + +.debug = [ args.get-arg debug-configuration ] ; + +# Initializes the brotli library. +# +# Options for configuring brotli:: +# +# +# The directory containing the brotli binaries. +# +# Overrides the default name of brotlicommon library. +# +# Overrides the default name of brotlidec library. +# +# Overrides the default name of brotlienc library. +# +# The directory containing the brotli headers. +# +# Extra directories to add to library search paths of consumers during +# runtime (multiple instances are allowed). +# +# If none of these options is specified, then the environmental +# variables BROTLI_LIBRARY_PATH, BROTLI_NAME, and BROTLI_INCLUDE will +# be used instead. +# +# Examples:: +# +# # Find brotli in the default system location +# using brotli ; +# # Find brotli in /usr/local +# using brotli : 1.1.0 +# : /usr/local/include /usr/local/lib ; +# +rule init ( + version ? + # (currently ignored) + + : options * + # A list of the options to use + + : requirements * + # The requirements for the target + + : is-default ? + ) +{ + local caller = [ project.current ] ; + + if ! $(.initialized) + { + .initialized = true ; + + project.initialize $(__name__) ; + .project = [ project.current ] ; + project brotli ; + } + + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local brotlicommon-name = [ feature.get-values : $(options) ] ; + local brotlidec-name = [ feature.get-values : $(options) ] ; + local brotlienc-name = [ feature.get-values : $(options) ] ; + local dll-paths = [ property.select : $(options) ] ; + + if ! $(options) + { + is-default = true ; + } + + condition = [ property-set.create $(requirements) ] ; + condition = [ property-set.create [ $(condition).base ] ] ; + + if $(.configured.$(condition)) + { + if $(is-default) + { + if $(.debug) + { + ECHO "notice: [brotli] brotli is already configured" ; + } + } + else + { + errors.user-error "brotli is already configured" ; + } + return ; + } + else + { + if $(.debug) + { + ECHO "notice: [brotli] Using pre-installed library" ; + if $(condition) + { + ECHO "notice: [brotli] Condition" [ $(condition).raw ] ; + } + } + + local brotlicommon = [ new ac-library brotlicommon : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlicommon-name) ] ; + $(brotlicommon).set-header $(header) ; + $(brotlicommon).set-default-names $(brotlicommon_names) ; + $(brotlicommon).add-usage-requirements $(dll-paths) ; + + local brotlidec = [ new ac-library brotlidec : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlidec-name) ] ; + $(brotlidec).set-header $(header) ; + $(brotlidec).set-default-names $(brotlidec_names) ; + $(brotlidec).add-usage-requirements $(dll-paths) ; + + local brotlienc = [ new ac-library brotlienc : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlienc-name) ] ; + $(brotlienc).set-header $(header) ; + $(brotlienc).set-default-names $(brotlienc_names) ; + $(brotlienc).add-usage-requirements $(dll-paths) ; + + targets.main-target-alternative $(brotlicommon) ; + targets.main-target-alternative $(brotlidec) ; + targets.main-target-alternative $(brotlienc) ; + } + .configured.$(condition) = true ; +} diff --git a/test/libbrotli.py b/test/libbrotli.py new file mode 100644 index 0000000000..ba6f949188 --- /dev/null +++ b/test/libbrotli.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +# Copy-paste-modify from libzstd.py +# Copyright (C) 2013 Steven Watanabe +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.bfgroup.xyz/b2/LICENSE.txt) + +import BoostBuild +import MockToolset + +t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0) + +MockToolset.create(t) + +# Generic definitions that aren't configuration specific +common_stuff = ''' +source_file('test.cpp', 'test.cpp') +source_file('main.cpp', 'int main() {}') +source_file('brotli/decode.h.cpp', '#include \\n') +action('-c -x c++ $main.cpp -o $main.o') +''' +t.write('test.cpp', 'test.cpp') + +for lib in ('brotlicommon', 'brotlidec', 'brotlienc'): + + # Default initialization - static library + t.rm('bin') + t.write("Jamroot.jam", """ +path-constant here : . ; +using brotli ; +exe test : test.cpp /brotli//%(lib)s : : static shared ; +""" % {'lib': lib}) + + MockToolset.set_expected(t, common_stuff + ''' +action('$main.o --static-lib=%(lib)s -o $config.exe') +action('-c -x c++ $brotli/decode.h.cpp -o $brotli/decode.h.o') +action('-c -x c++ $test.cpp -o $test.o') +action('$test.o --static-lib=%(lib)s -o $test') +''' % {'lib': lib}) + t.run_build_system() + t.expect_addition('bin/mock/debug/test.exe') + t.expect_addition('bin/mock/debug/link-static/test.exe') + + # Default initialization - shared library + t.rm('bin') + t.write("Jamroot.jam", """ +path-constant here : . ; +using brotli ; +exe test : test.cpp /brotli//%(lib)s : : static shared ; +""" % {'lib': lib}) + + MockToolset.set_expected(t, common_stuff + ''' +action('$main.o --shared-lib=%(lib)s -o $config.exe') +action('-c -x c++ $brotli/decode.h.cpp -o $brotli/decode.h.o') +action('-c -x c++ $test.cpp -o $test.o') +action('$test.o --shared-lib=%(lib)s -o $test') +''' % {'lib': lib}) + t.run_build_system() + t.expect_addition('bin/mock/debug/test.exe') + t.expect_addition('bin/mock/debug/link-static/test.exe') + + # Initialization in explicit location - static library + t.rm('bin') + t.write("Jamroot.jam", """ +path-constant here : . ; +using brotli : : <%(lib)s-name>my%(lib)s $(here)/brotli $(here)/brotli ; +exe test : test.cpp /brotli//%(lib)s : : static shared ; +""" % {'lib': lib}) + + t.write('brotli/brotli/decode.h', 'brotli') + + MockToolset.set_expected(t, common_stuff + ''' +action('$main.o -L./brotli --static-lib=my%(lib)s -o $config.exe') +action('-c -x c++ $test.cpp -I./brotli -o $test.o') +action('$test.o -L./brotli --static-lib=my%(lib)s -o $test') +''' % {'lib': lib}) + t.run_build_system() + t.expect_addition('bin/mock/debug/test.exe') + t.expect_addition('bin/mock/debug/link-static/test.exe') + + # Initialization in explicit location - shared library + t.rm('bin') + t.write("Jamroot.jam", """ +path-constant here : . ; +using brotli : : <%(lib)s-name>my%(lib)s $(here)/brotli $(here)/brotli ; +exe test : test.cpp /brotli//%(lib)s : : static shared ; +""" % {'lib': lib}) + + MockToolset.set_expected(t, common_stuff + ''' +action('$main.o -L./brotli --shared-lib=my%(lib)s -o $config.exe') +action('-c -x c++ $test.cpp -I./brotli -o $test.o') +action('$test.o -L./brotli --shared-lib=my%(lib)s -o $test') +''' % {'lib': lib}) + t.run_build_system() + t.expect_addition('bin/mock/debug/test.exe') + t.expect_addition('bin/mock/debug/link-static/test.exe') + + # Initialization in explicit location - both static and shared libraries + t.rm('bin') + t.write("Jamroot.jam", """ +path-constant here : . ; +using brotli : : <%(lib)s-name>my%(lib)s $(here)/brotli $(here)/brotli ; +exe test : test.cpp /brotli//%(lib)s + : shared:SHARED : static shared ; +""" % {'lib': lib}) + + MockToolset.set_expected(t, common_stuff + ''' +action('$main.o -L./brotli --static-lib=my%(lib)s -o $config.exe') +action('$main.o -L./brotli --shared-lib=my%(lib)s -o $config.exe') +action('-c -x c++ $test.cpp -I./brotli -o $test-static.o') +action('-c -x c++ $test.cpp -I./brotli -DSHARED -o $test-shared.o') +action('$test-static.o -L./brotli --static-lib=my%(lib)s -o $test') +action('$test-shared.o -L./brotli --shared-lib=my%(lib)s -o $test') +''' % {'lib': lib}) + t.run_build_system() + t.expect_addition('bin/mock/debug/test.exe') + t.expect_addition('bin/mock/debug/link-static/test.exe') + +t.cleanup() diff --git a/test/test_all.py b/test/test_all.py index 496b50bd9e..5ebb3d9615 100755 --- a/test/test_all.py +++ b/test/test_all.py @@ -410,6 +410,7 @@ def reorder_tests(tests, first_test): "inline", "install_build_no", "lang_asm", + "libbrotli", "lib_source_property", "lib_zlib", "libjpeg",