From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 990B16EC5E; Fri, 9 Apr 2021 22:55:10 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 990B16EC5E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1617998110; bh=qNREVUt7CreNjd2iSw/SXJyPnP6phiK74GvBFt3iXfg=; h=In-Reply-To:Date:Cc:References:To:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=TRyTRzu+AenjbMezbUx+1nCv2L7n+wGhNS5E4sttwQSPhyTL4GK3U6K33g8JlPQww AE8oYEUxYjyWIQ25alaYAqkkUxfcRrKvoqjrUJylZf6nxGmlLgaLCxrIZEaSo+w7TO lVvZtVirB+eufFI9Jk+9Y5F+T9TNiZjB6uAuohac= Received: from smtp44.i.mail.ru (smtp44.i.mail.ru [94.100.177.104]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 28BF76EC60 for ; Fri, 9 Apr 2021 22:54:46 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 28BF76EC60 Received: by smtp44.i.mail.ru with esmtpa (envelope-from ) id 1lUxDJ-0006AJ-5A; Fri, 09 Apr 2021 22:54:45 +0300 Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.1\)) In-Reply-To: <20210331235626.wlocbeps5cn7p6za@tkn_work_nb> Date: Fri, 9 Apr 2021 22:54:44 +0300 Cc: tarantool-patches@dev.tarantool.org Content-Transfer-Encoding: quoted-printable Message-Id: <5ABCF6D8-EC4B-424B-A920-29A80798827A@tarantool.org> References: <20210319141308.98726-1-roman.habibov@tarantool.org> <20210319141308.98726-2-roman.habibov@tarantool.org> <20210331235626.wlocbeps5cn7p6za@tkn_work_nb> To: Alexander Turenko , Leonid Vasiliev X-Mailer: Apple Mail (2.3608.120.23.2.1) X-7564579A: 78E4E2B564C1792B X-77F55803: 4F1203BC0FB41BD92FFCB8E6708E7480EBD5CA77A668ECB87DA2124B0A8E6609182A05F5380850406449543C9C32754DDD405B0CB79C18F7FD7D0D0147FBAEF3679AA6BC6F7E63DD X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE74FF5DF51D335CFFFEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F79006375FE8AD9F0D2764EB8638F802B75D45FF914D58D5BE9E6BC1A93B80C6DEB9DEE97C6FB206A91F05B231B5652026261692EFEC568F2F42CF76267281413F282BCCD2E47CDBA5A96583C09775C1D3CA48CFCA5A41EBD8A3A0199FA2833FD35BB23D2EF20D2F80756B5F868A13BD56FB6657A471835C12D1D977725E5C173C3A84C317B107DEF921CE79117882F4460429728AD0CFFFB425014E868A13BD56FB6657D81D268191BDAD3DC09775C1D3CA48CF5A44E68B9071C39E76E601842F6C81A12EF20D2F80756B5F7E9C4E3C761E06A776E601842F6C81A127C277FBC8AE2E8BA3B096B90FE234BF3AA81AA40904B5D99C9F4D5AE37F343A13A31611C1FE51D268655334FD4449CB9ECD01F8117BC8BEAAAE862A0553A39223F8577A6DFFEA7CB107C85ADE6F3D0443847C11F186F3C59DAA53EE0834AAEE X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A2368A440D3B0F6089093C9A16E5BC824A2A04A2ABAA09D25379311020FFC8D4ADED27546C620ED14964CF93BAC17A6C03 X-C1DE0DAB: 0D63561A33F958A5AA3D09A272C21058335B3E6AA92A6673C2C775AB7093DA0DD59269BC5F550898D99A6476B3ADF6B47008B74DF8BB9EF7333BD3B22AA88B938A852937E12ACA7502E6951B79FF9A3F410CA545F18667F91A7EA1CDA0B5A7A0 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34F3735C80F9F4B96D291B48A7E579C13117543637D553FAE76C2EEB47B196CAE188EEC89C294C851A1D7E09C32AA3244C8441C2748C6A6BEBEE03F9CFC8D5DE4EA90944CA99CF22E3FACE5A9C96DEB163 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojyO2lHpuZu4SsJwVf8dw9YQ== X-Mailru-Sender: ED747E36EB90C325A4602F7CA2CC58C1779F7429B93E716CB71CB68B7D9264FA3FB1763410276AF21EC3B765AEBF8DFDC77752E0C033A69EF2501F26BCC01020D1D7FFF4A7B59B3C6C18EFA0BB12DBB0 X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH 1/3] build: export libCURL symbols X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Roman Khabibov via Tarantool-patches Reply-To: Roman Khabibov Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" > On Apr 1, 2021, at 02:56, Alexander Turenko = wrote: >=20 > On Fri, Mar 19, 2021 at 05:13:06PM +0300, Roman Khabibov wrote: >> Export the symbols to tarantool executable in the case of libCURL >> included as bundled library. >>=20 >> This patch is just 1.10 adaptation of the original commit 9fc57c4 >> performed by Yaroslav Dynnikov . >=20 > The commit hash you reference is not reachable from the master branch. Fixed. > Aside of this, some logic arrives from > 47c19eeb4f67cb7257ce32542443c09410b6e752 ('build: don't re-export > libcurl.so/dylib symbols'). >=20 >> diff --git a/extra/curl_symbols b/extra/curl_symbols >> new file mode 100755 >> index 000000000..89e247a00 >> --- /dev/null >> +++ b/extra/curl_symbols >> @@ -0,0 +1,81 @@ >> +curl_easy_cleanup >> +curl_easy_duphandle >> +curl_easy_escape >=20 > It is the part of extra/exports, so I would name it > extra/exports_libcurl. >=20 > I would also keep Yaroslav's comment at the start: >=20 > | # The following list was obtained by parsing libcurl.a static = library: > | # nm libcurl.a | grep -oP 'T \K(curl_.+)$' | sort > | > | curl_easy_cleanup > | curl_easy_duphandle > | <...> diff --git a/extra/exports_libcurl b/extra/exports_libcurl new file mode 100755 index 000000000..d9f420d03 --- /dev/null +++ b/extra/exports_libcurl @@ -0,0 +1,84 @@ +# The following list was obtained by parsing libcurl.a static +# library: nm libcurl.a | grep -oP 'T \K(curl_.+)$' | sort + >> diff --git a/extra/mkexports b/extra/mkexports >> index 145e5b8ce..c10f20ae4 100755 >> --- a/extra/mkexports >> +++ b/extra/mkexports >> @@ -2,22 +2,30 @@ >> # $1 - in file >> # $2 - out file >> # $3 - os >> -# $4 - export templates >> +# $4 - is bundled curl on/off flag >> +# $5 - curl symbols >> +# $6 - export templates >=20 > I would just pass one or two files (extra/exports, > extra/exports_libcurl) in $1: it'll be easier to read. >=20 > My diff (from 1.10): >=20 > | diff --git a/extra/mkexports b/extra/mkexports > | index 145e5b8ce..95c3f8eed 100755 > | --- a/extra/mkexports > | +++ b/extra/mkexports > | @@ -1,5 +1,5 @@ > | #! /bin/sh > | -# $1 - in file > | +# $1 - in file(s) > | # $2 - out file > | # $3 - os > | # $4 - export templates > | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt > | index 1e840aab9..db771cce9 100644 > | --- a/src/CMakeLists.txt > | +++ b/src/CMakeLists.txt > | @@ -301,6 +301,13 @@ if(BUILD_STATIC) > | endif() > | endif() > | =20 > | +set(exports_file_sources ${CMAKE_SOURCE_DIR}/extra/exports) > | +if (EXPORT_LIBCURL_SYMBOLS) > | + set(exports_file_sources ${exports_file_sources} > | + ${CMAKE_SOURCE_DIR}/extra/exports_libcurl) > | +endif() > | +string(REPLACE ";" " " exports_file_sources = "${exports_file_sources}") > | + > | # Exports syntax is toolchain-dependent, preprocessing is necessary > | set(exports_file = ${CMAKE_BINARY_DIR}/extra/exports.${CMAKE_SYSTEM_NAME}) > | add_custom_target(preprocess_exports > | @@ -309,7 +316,7 @@ add_custom_command( > | OUTPUT ${exports_file} > | DEPENDS ${CMAKE_SOURCE_DIR}/extra/exports > | COMMAND ${CMAKE_SOURCE_DIR}/extra/mkexports > | - ${CMAKE_SOURCE_DIR}/extra/exports > | + ${exports_file_sources} > | ${exports_file} ${CMAKE_SYSTEM_NAME} > | ${EXPORT_LIST} > | ) I choose it. > ---- >=20 > Just for the record: I also tried another implementation variant: >=20 > | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt > | index 1e840aab9..6804ba301 100644 > | --- a/src/CMakeLists.txt > | +++ b/src/CMakeLists.txt > | @@ -284,8 +284,9 @@ if(BUILD_STATIC) > | list(APPEND EXPORT_LIST ${SYMBOLS_LIB}) > | # set variable to allow rescan (CMake depended) > | set(SYMBOLS_LIB "SYMBOLS_LIB-NOTFOUND") > | - elseif (${libstatic} STREQUAL bundled-libcurl OR > | - ${libstatic} STREQUAL bundled-ares) > | + elseif (${libstatic} STREQUAL bundled-libcurl) > | + # Handled below. > | + elseif (${libstatic} STREQUAL bundled-ares) > | message("We don't need to export symbols from = statically linked ${libstatic}, skipped") > | else() > | message(WARNING "${libstatic} should be a static") > | @@ -301,10 +302,24 @@ if(BUILD_STATIC) > | endif() > | endif() > | =20 > | +# Expose libcurl symbols. > | +if(ENABLE_BUNDLED_LIBCURL OR BUILD_STATIC) > | + set(reexport_libraries ${reexport_libraries} ${CURL_LIBRARIES}) > | + > | + if (ENABLE_BUNDLED_LIBCURL) > | + get_target_property(libstatic bundled-libcurl = IMPORTED_LOCATION) > | + string(REGEX REPLACE "libcurl.a" "libcurl.so" libdynamic = ${libstatic}) > | + else() > | + find_library(libdynamic NAMES curl) > | + endif() > | + > | + set(EXPORT_LIST ${EXPORT_LIST} ${libdynamic}) > | +endif() > | + > | # Exports syntax is toolchain-dependent, preprocessing is necessary > | set(exports_file = ${CMAKE_BINARY_DIR}/extra/exports.${CMAKE_SYSTEM_NAME}) > | add_custom_target(preprocess_exports > | - DEPENDS ${exports_file}) > | + DEPENDS ${exports_file} build_bundled_libs) > | add_custom_command( > | OUTPUT ${exports_file} > | DEPENDS ${CMAKE_SOURCE_DIR}/extra/exports >=20 > (At least it works for libcurl bundling, but I didn't verified it with = static > build.) >=20 > However explicit listing of public libcurl symbols looks easier to = understand > and in tune with master. commit aee16d72d6740f2b5b18c034ab0ec0f0379745f3 Author: Roman Khabibov Date: Fri Jan 15 01:07:55 2021 +0300 build: export libcurl symbols =20 Export the symbols to tarantool executable in the case of libcurl included as bundled library. =20 This patch is just 1.10 adaptation of the original commit 29ec628 performed by Yaroslav Dynnikov . diff --git a/CMakeLists.txt b/CMakeLists.txt index 84e019eb0..7e2ddb503 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,6 +347,16 @@ else() find_package(CURL) endif() =20 +# +# Export libcurl symbols if the library is bundled. +# +if (ENABLE_BUNDLED_LIBCURL) + set(EXPORT_LIBCURL_SYMBOLS ON) +else() + set(EXPORT_LIBCURL_SYMBOLS OFF) +endif() +message(STATUS "EXPORT_LIBCURL_SYMBOLS: ${EXPORT_LIBCURL_SYMBOLS}") + # # ReadLine # diff --git a/extra/exports_libcurl b/extra/exports_libcurl new file mode 100755 index 000000000..d9f420d03 --- /dev/null +++ b/extra/exports_libcurl @@ -0,0 +1,84 @@ +# The following list was obtained by parsing libcurl.a static +# library: nm libcurl.a | grep -oP 'T \K(curl_.+)$' | sort + +curl_easy_cleanup +curl_easy_duphandle +curl_easy_escape +curl_easy_getinfo +curl_easy_init +curl_easy_pause +curl_easy_perform +curl_easy_recv +curl_easy_reset +curl_easy_send +curl_easy_setopt +curl_easy_strerror +curl_easy_unescape +curl_easy_upkeep +curl_escape +curl_formadd +curl_formfree +curl_formget +curl_free +curl_getdate +curl_getenv +curl_global_cleanup +curl_global_init +curl_global_init_mem +curl_global_sslset +curl_maprintf +curl_mfprintf +curl_mime_addpart +curl_mime_data +curl_mime_data_cb +curl_mime_encoder +curl_mime_filedata +curl_mime_filename +curl_mime_free +curl_mime_headers +curl_mime_init +curl_mime_name +curl_mime_subparts +curl_mime_type +curl_mprintf +curl_msnprintf +curl_msprintf +curl_multi_add_handle +curl_multi_assign +curl_multi_cleanup +curl_multi_fdset +curl_multi_info_read +curl_multi_init +curl_multi_perform +curl_multi_poll +curl_multi_remove_handle +curl_multi_setopt +curl_multi_socket +curl_multi_socket_action +curl_multi_socket_all +curl_multi_strerror +curl_multi_timeout +curl_multi_wait +curl_mvaprintf +curl_mvfprintf +curl_mvprintf +curl_mvsnprintf +curl_mvsprintf +curl_pushheader_byname +curl_pushheader_bynum +curl_share_cleanup +curl_share_init +curl_share_setopt +curl_share_strerror +curl_slist_append +curl_slist_free_all +curl_strequal +curl_strnequal +curl_unescape +curl_url +curl_url_cleanup +curl_url_dup +curl_url_get +curl_url_set +curl_version +curl_version_info diff --git a/extra/mkexports b/extra/mkexports index 145e5b8ce..15bd8e727 100755 --- a/extra/mkexports +++ b/extra/mkexports @@ -1,6 +1,6 @@ #! /bin/sh # $1 - in file -# $2 - out file +# $2 - out file(s) # $3 - os # $4 - export templates if [ "x$3x" =3D xDarwinx ]; then diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04289af3d..c2d3e7fcd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -231,13 +231,12 @@ target_link_libraries(server core bit uri uuid = ${ICU_LIBRARIES}) # Rule of thumb: if exporting a symbol from a static library, list the # library here. set (reexport_libraries server core misc bitset csv - ${LUAJIT_LIBRARIES} ${MSGPUCK_LIBRARIES} ${ICU_LIBRARIES}) + ${LUAJIT_LIBRARIES} ${MSGPUCK_LIBRARIES} ${ICU_LIBRARIES} = ${CURL_LIBRARIES}) =20 set (common_libraries ${reexport_libraries} ${LIBYAML_LIBRARIES} ${READLINE_LIBRARIES} - ${CURL_LIBRARIES} ${ICONV_LIBRARIES} ${OPENSSL_LIBRARIES} ) @@ -300,6 +299,13 @@ if(BUILD_STATIC) endif() endif() =20 +set(exports_file_sources ${CMAKE_SOURCE_DIR}/extra/exports) +if (EXPORT_LIBCURL_SYMBOLS) + set(exports_file_sources ${exports_file_sources} + ${CMAKE_SOURCE_DIR}/extra/exports_libcurl) +endif() +string(REPLACE ";" " " exports_file_sources "${exports_file_sources}") + # Exports syntax is toolchain-dependent, preprocessing is necessary set(exports_file = ${CMAKE_BINARY_DIR}/extra/exports.${CMAKE_SYSTEM_NAME}) add_custom_target(preprocess_exports @@ -308,7 +314,7 @@ add_custom_command( OUTPUT ${exports_file} DEPENDS ${CMAKE_SOURCE_DIR}/extra/exports COMMAND ${CMAKE_SOURCE_DIR}/extra/mkexports - ${CMAKE_SOURCE_DIR}/extra/exports + ${exports_file_sources} ${exports_file} ${CMAKE_SYSTEM_NAME} ${EXPORT_LIST} ) diff --git a/test/box-tap/curl-build.test.lua = b/test/box-tap/curl-build.test.lua new file mode 100755 index 000000000..300d60b07 --- /dev/null +++ b/test/box-tap/curl-build.test.lua @@ -0,0 +1,177 @@ +#!/usr/bin/env tarantool + +local tap =3D require('tap') +local ffi =3D require('ffi') +ffi.cdef([[ + void *dlsym(void *handle, const char *symbol); + struct curl_version_info_data { + int age; /* see description below */ + const char *version; /* human readable string */ + unsigned int version_num; /* numeric representation */ + const char *host; /* human readable string */ + int features; /* bitmask, see below */ + char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used, always zero */ + const char *libz_version; /* human readable string */ + const char * const *protocols; /* protocols */ + + /* when 'age' is CURLVERSION_SECOND or higher, the members = below exist */ + const char *ares; /* human readable string */ + int ares_num; /* number */ + + /* when 'age' is CURLVERSION_THIRD or higher, the members below = exist */ + const char *libidn; /* human readable string */ + + /* when 'age' is CURLVERSION_FOURTH or higher (>=3D 7.16.1), = the members + below exist */ + int iconv_ver_num; /* '_libiconv_version' if iconv = support enabled */ + + const char *libssh_version; /* human readable string */ + + /* when 'age' is CURLVERSION_FIFTH or higher (>=3D 7.57.0), the = members + below exist */ + unsigned int brotli_ver_num; /* Numeric Brotli version + (MAJOR << 24) | (MINOR << 12) | = PATCH */ + const char *brotli_version; /* human readable string. */ + + /* when 'age' is CURLVERSION_SIXTH or higher (>=3D 7.66.0), the = members + below exist */ + unsigned int nghttp2_ver_num; /* Numeric nghttp2 version + (MAJOR << 16) | (MINOR << 8) | = PATCH */ + const char *nghttp2_version; /* human readable string. */ + + const char *quic_version; /* human readable quic (+ HTTP/3) = library + + version or NULL */ + + /* when 'age' is CURLVERSION_SEVENTH or higher (>=3D 7.70.0), = the members + below exist */ + const char *cainfo; /* the built-in default = CURLOPT_CAINFO, might + be NULL */ + const char *capath; /* the built-in default = CURLOPT_CAPATH, might + be NULL */ + }; + + struct curl_version_info_data *curl_version_info(int age); +]]) + +local info =3D ffi.C.curl_version_info(7) +local test =3D tap.test('curl-features') +test:plan(3) + +if test:ok(info.ssl_version ~=3D nil, 'Curl built with SSL support') = then + test:diag('ssl_version: ' .. ffi.string(info.ssl_version)) +end +if test:ok(info.libz_version ~=3D nil, 'Curl built with LIBZ') then + test:diag('libz_version: ' .. ffi.string(info.libz_version)) +end + +local RTLD_DEFAULT +-- See `man 3 dlsym`: +-- RTLD_DEFAULT +-- Find the first occurrence of the desired symbol using the = default +-- shared object search order. The search will include global = symbols +-- in the executable and its dependencies, as well as symbols in = shared +-- objects that were dynamically loaded with the RTLD_GLOBAL flag. +if jit.os =3D=3D "OSX" then + RTLD_DEFAULT =3D ffi.cast("void *", -2LL) +else + RTLD_DEFAULT =3D ffi.cast("void *", 0LL) +end + +-- The following list was obtained by parsing libcurl.a static library: +-- nm libcurl.a | grep -oP 'T \K(curl_.+)$' | sort +local curl_symbols =3D { + 'curl_easy_cleanup', + 'curl_easy_duphandle', + 'curl_easy_escape', + 'curl_easy_getinfo', + 'curl_easy_init', + 'curl_easy_pause', + 'curl_easy_perform', + 'curl_easy_recv', + 'curl_easy_reset', + 'curl_easy_send', + 'curl_easy_setopt', + 'curl_easy_strerror', + 'curl_easy_unescape', + 'curl_easy_upkeep', + 'curl_escape', + 'curl_formadd', + 'curl_formfree', + 'curl_formget', + 'curl_free', + 'curl_getdate', + 'curl_getenv', + 'curl_global_cleanup', + 'curl_global_init', + 'curl_global_init_mem', + 'curl_global_sslset', + 'curl_maprintf', + 'curl_mfprintf', + 'curl_mime_addpart', + 'curl_mime_data', + 'curl_mime_data_cb', + 'curl_mime_encoder', + 'curl_mime_filedata', + 'curl_mime_filename', + 'curl_mime_free', + 'curl_mime_headers', + 'curl_mime_init', + 'curl_mime_name', + 'curl_mime_subparts', + 'curl_mime_type', + 'curl_mprintf', + 'curl_msnprintf', + 'curl_msprintf', + 'curl_multi_add_handle', + 'curl_multi_assign', + 'curl_multi_cleanup', + 'curl_multi_fdset', + 'curl_multi_info_read', + 'curl_multi_init', + 'curl_multi_perform', + 'curl_multi_poll', + 'curl_multi_remove_handle', + 'curl_multi_setopt', + 'curl_multi_socket', + 'curl_multi_socket_action', + 'curl_multi_socket_all', + 'curl_multi_strerror', + 'curl_multi_timeout', + 'curl_multi_wait', + 'curl_mvaprintf', + 'curl_mvfprintf', + 'curl_mvprintf', + 'curl_mvsnprintf', + 'curl_mvsprintf', + 'curl_pushheader_byname', + 'curl_pushheader_bynum', + 'curl_share_cleanup', + 'curl_share_init', + 'curl_share_setopt', + 'curl_share_strerror', + 'curl_slist_append', + 'curl_slist_free_all', + 'curl_strequal', + 'curl_strnequal', + 'curl_unescape', + 'curl_url', + 'curl_url_cleanup', + 'curl_url_dup', + 'curl_url_get', + 'curl_url_set', + 'curl_version', + 'curl_version_info', +} + +test:test('curl_symbols', function(t) + t:plan(#curl_symbols) + for _, sym in ipairs(curl_symbols) do + t:ok( + ffi.C.dlsym(RTLD_DEFAULT, sym) ~=3D nil, + ('Symbol %q found'):format(sym) + ) + end +end) + +os.exit(test:check() and 0 or 1)