mirror of
https://https.git.savannah.gnu.org/git/m4.git
synced 2026-01-29 02:44:21 +00:00
* m4/module.c (m4__module_open): Instead of calling lt_dlopenext right away, use the preload hint first incase path searching has been disabled by POSIXLY_CORRECT. Otherwise fallback to a manual path search to override libltdl's algorithm. * m4/m4module.h: Adjust. * m4/path.c (FILE_SUFFIXES): Order to try suffixes in our path search algorithm. (NO_SUFFIXES): Alternatively, how to to search a path without adding file suffixes. (m4_path_search): Add a new suffixes parameter defaulting to NO_SUFFIXES. Adjust all callers. (m4_load_filename): New generic load function that tries to open a matched file first as a module, and if that fails fall back to as (possibly frozen) m4 input. (m4_fopen): New function with close on exec functionality. * modules/m4.c (include, sinclude): Use m4_load_filename to overload these builtins to work with modules too. * modules/load.c (load): Removed this builtin. * src/main.c: Remove Dynamic loading features section, and M4MODPATH support. (long_options): Remove "load-module", "unload-module" and "module-directory" options. (OPTSTRING): Remove 'M' and 'm' options. (process_file): Use m4_load_filename to support module names on the command line. * Makefile.am (TESTS_ENVIRONMENT): No need for abs_top_builddir any more. * doc/m4.texinfo: Update examples. (Dynamic loading features, Load): Removed. (Command line files): Describe how non-option arguments are treated to try to find a matching macro file or dso. (Include): Describe additional dso loading features if a suitable text file cannot be found. (Modules): Updated. * tests/builtins.at, tests/m4.in, tests/modules.at, tests/options.at: Adjust test cases and calling conventions to new semantics. * NEWS: Updated.
569 lines
10 KiB
Plaintext
569 lines
10 KiB
Plaintext
# Hand crafted tests for GNU M4. -*- Autotest -*-
|
|
# Copyright (C) 2001, 2006, 2007, 2008 Free Software Foundation, Inc.
|
|
|
|
# This file is part of GNU M4.
|
|
#
|
|
# GNU M4 is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# GNU M4 is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
AT_BANNER([Module support.])
|
|
|
|
|
|
## --------- ##
|
|
## modfreeze ##
|
|
## --------- ##
|
|
|
|
AT_SETUP([Freezing modules])
|
|
AT_KEYWORDS([frozen])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
AT_DATA([[frozen.m4]],
|
|
[[divert(1)dnl
|
|
define(`test', `local::`test'')dnl
|
|
define(`test1', defn(`test'))dnl
|
|
->test
|
|
include(`modtest')
|
|
define(`test2', defn(`test'))dnl
|
|
->test
|
|
include(`shadow')
|
|
define(`test3', defn(`test'))dnl
|
|
->test
|
|
]])
|
|
|
|
AT_DATA([[unfrozen.m4]],
|
|
[[undivert(1)dnl
|
|
test1
|
|
test2
|
|
test3
|
|
]])
|
|
|
|
# First generate the `expout' ouput by running over the sources before
|
|
# freezing.
|
|
AT_CHECK_M4([-I "$abs_builddir" load frozen.m4 unfrozen.m4],
|
|
[0], [stdout], [stderr])
|
|
|
|
mv stdout expout
|
|
mv stderr experr
|
|
|
|
# Now freeze the first source file.
|
|
AT_CHECK_M4([-I "$abs_builddir" load -F frozen.m4f frozen.m4],
|
|
[0], [], [ignore])
|
|
|
|
# Now rerun the original sequence, but using the frozen file.
|
|
AT_CHECK_M4([-I "$abs_builddir" -R frozen.m4f unfrozen.m4],
|
|
[0], [expout], [experr])
|
|
|
|
AT_CLEANUP([frozen.m4f])
|
|
|
|
|
|
## ------------------ ##
|
|
## module test macros ##
|
|
## ------------------ ##
|
|
|
|
AT_SETUP([module test macros])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
AT_CHECK_GMP
|
|
|
|
AT_DATA([in], [[include(`mpeval')
|
|
-__load__-__mpeval__-
|
|
unload(`mpeval')
|
|
-__load__-__mpeval__-
|
|
unload(`load')
|
|
-__load__-__mpeval__-
|
|
]])
|
|
|
|
AT_CHECK_M4([load in], [0], [[
|
|
---
|
|
|
|
--__mpeval__-
|
|
|
|
-__load__-__mpeval__-
|
|
]])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
## ---------------------------- ##
|
|
## Exercising the test module. ##
|
|
## ---------------------------- ##
|
|
|
|
# AT_CHECK_M4_MODTEST(TITLE, ENV-VARS, M4-OPTIONS)
|
|
# ------------------------------------------------
|
|
# Add a test named TITLE, running m4 with either ENV-VARS in the environment
|
|
# or M4-OPTIONS set to pick up test modules.
|
|
m4_define([AT_CHECK_M4_MODTEST],
|
|
[AT_SETUP([$1])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
AT_DATA([input.m4],
|
|
[[include(`modtest')
|
|
test
|
|
Dumpdef: dumpdef(`test').
|
|
unload(`modtest')
|
|
test
|
|
Dumpdef: dumpdef(`test').
|
|
]])
|
|
|
|
dnl Fortunately, all tests within AT_SETUP are in the same subshell, so
|
|
dnl setting the environment now will impact the AT_CHECK_M4, but not
|
|
dnl carry over to the next AT_SETUP.
|
|
m4_ifval([$2], [$2
|
|
export m4_substr([$2], [0], m4_index([$2], [=]))])
|
|
|
|
AT_CHECK_M4([load $3 input.m4], [0],
|
|
[[
|
|
Test module called.
|
|
Dumpdef: .
|
|
|
|
test
|
|
Dumpdef: .
|
|
]],
|
|
[[Test module loaded.
|
|
test: <test>
|
|
Test module unloaded.
|
|
m4:input.m4:6: Warning: dumpdef: undefined macro `test'
|
|
]])
|
|
|
|
AT_CLEANUP
|
|
])
|
|
|
|
|
|
|
|
AT_CHECK_M4_MODTEST([--include: absolute path],
|
|
[], [-I "$abs_builddir"])
|
|
|
|
AT_CHECK_M4_MODTEST([--include: relative path],
|
|
[], [-I "$top_build_prefix/tests"])
|
|
|
|
AT_CHECK_M4_MODTEST([M4PATH: absolute path],
|
|
[M4PATH="$abs_builddir"], [])
|
|
|
|
AT_CHECK_M4_MODTEST([M4PATH: relative path],
|
|
[M4PATH="$top_build_prefix/tests"], [])
|
|
|
|
|
|
|
|
## ------ ##
|
|
## shadow ##
|
|
## ------ ##
|
|
|
|
AT_SETUP([modules: shadow])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
AT_DATA([[input.m4]],
|
|
[[# no modules loaded yet
|
|
test
|
|
shadow
|
|
|
|
# define our own macros for `test' and `shadow'
|
|
define(`test', `local::`test'')
|
|
define(`shadow', `local::`shadow'')
|
|
test
|
|
shadow
|
|
|
|
# module Shadow defines `shadow' and `test' macros
|
|
include(`shadow')
|
|
dumpdef(`test')
|
|
dumpdef(`shadow')
|
|
test
|
|
shadow
|
|
|
|
# save the definition of `test' from the Shadow module
|
|
define(`Shadow::test', defn(`test'))
|
|
|
|
# module Modtest also defines a `test' macro
|
|
include(`modtest')
|
|
dumpdef(`test')
|
|
dumpdef(`shadow')
|
|
test
|
|
shadow
|
|
|
|
# Reloading Shadow shouldn't affect anything
|
|
include(`shadow')
|
|
dumpdef(`test')
|
|
dumpdef(`shadow')
|
|
test
|
|
shadow
|
|
|
|
# Unloading Modtest will unshadow the test definition in Shadow
|
|
unload(`modtest')
|
|
dumpdef(`test')
|
|
dumpdef(`shadow')
|
|
test
|
|
shadow
|
|
|
|
# Unloading Shadow once has no effect (we loaded it twice)
|
|
unload(`shadow')
|
|
dumpdef(`test')
|
|
dumpdef(`shadow')
|
|
test
|
|
shadow
|
|
|
|
# Unloading Shadow again will revert to copying `test' and the local
|
|
# `shadow' macro.
|
|
unload(`shadow')
|
|
test
|
|
shadow
|
|
]])
|
|
|
|
AT_DATA([[expout]],
|
|
[[# no modules loaded yet
|
|
test
|
|
shadow
|
|
|
|
# define our own macros for `test' and `shadow'
|
|
|
|
|
|
local::test
|
|
local::shadow
|
|
|
|
# module Shadow defines `shadow' and `test' macros
|
|
Shadow module loaded.
|
|
|
|
|
|
Shadow::test called.
|
|
Shadow::shadow called.
|
|
|
|
# save the definition of `test' from the Shadow module
|
|
|
|
|
|
# module Modtest also defines a `test' macro
|
|
|
|
|
|
|
|
Test module called.
|
|
Shadow::shadow called.
|
|
|
|
# Reloading Shadow shouldn't affect anything
|
|
|
|
|
|
|
|
Test module called.
|
|
Shadow::shadow called.
|
|
|
|
# Unloading Modtest will unshadow the test definition in Shadow
|
|
|
|
|
|
|
|
Shadow::test called.
|
|
Shadow::shadow called.
|
|
|
|
# Unloading Shadow once has no effect (we loaded it twice)
|
|
|
|
|
|
|
|
Shadow::test called.
|
|
Shadow::shadow called.
|
|
|
|
# Unloading Shadow again will revert to copying `test' and the local
|
|
# `shadow' macro.
|
|
|
|
local::test
|
|
local::shadow
|
|
]])
|
|
|
|
AT_DATA([[experr]],
|
|
[[test: <test>
|
|
shadow: <shadow>
|
|
Test module loaded.
|
|
test: <test>
|
|
shadow: <shadow>
|
|
test: <test>
|
|
shadow: <shadow>
|
|
Test module unloaded.
|
|
test: <test>
|
|
shadow: <shadow>
|
|
test: <test>
|
|
shadow: <shadow>
|
|
]])
|
|
|
|
AT_CHECK_M4([-I "$abs_builddir" load input.m4], [0],
|
|
[expout], [experr])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ------ ##
|
|
## unload ##
|
|
## ------ ##
|
|
|
|
AT_SETUP([modules: unload])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
AT_DATA([[input.m4]],
|
|
[[test
|
|
__test__
|
|
include(`modtest')
|
|
test
|
|
__test__
|
|
include(`shadow')
|
|
test
|
|
__test__
|
|
unload(`modtest')
|
|
test
|
|
__test__
|
|
include(`modtest')
|
|
test
|
|
__test__
|
|
unload(`modtest')
|
|
test
|
|
__test__
|
|
unload(`shadow')
|
|
test
|
|
__test__
|
|
]])
|
|
|
|
AT_DATA([[expout]],
|
|
[[test
|
|
__test__
|
|
|
|
Test module called.
|
|
modtest
|
|
Shadow module loaded.
|
|
Shadow::test called.
|
|
shadow
|
|
|
|
Shadow::test called.
|
|
shadow
|
|
|
|
Test module called.
|
|
modtest
|
|
|
|
Shadow::test called.
|
|
shadow
|
|
|
|
test
|
|
__test__
|
|
]])
|
|
|
|
AT_DATA([[experr]],
|
|
[[Test module loaded.
|
|
Test module unloaded.
|
|
Test module loaded.
|
|
Test module unloaded.
|
|
]])
|
|
|
|
|
|
AT_CHECK_M4([-I "$abs_builddir" load input.m4],
|
|
[0], [expout], [experr])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ----------------------- ##
|
|
## module symbol importing ##
|
|
## ----------------------- ##
|
|
|
|
# Importing a symbol from a not yet loaded module
|
|
|
|
# This test is ugly, because we need to canonicalize strerror strings
|
|
# to match our test. So we save STDERR to a file, and run another check
|
|
# which edits that file and compares it to the canonical STDERR output
|
|
# from the first command:
|
|
|
|
AT_SETUP([modules: importing])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
AT_DATA([[input.m4]],
|
|
[[import
|
|
include(`import')
|
|
import
|
|
unload(`modtest')
|
|
import
|
|
symbol_fail
|
|
module_fail
|
|
]])
|
|
|
|
AT_DATA([[expout]],
|
|
[[import
|
|
|
|
import::import called.
|
|
|
|
import::import called.
|
|
import::symbol_fail called.
|
|
]])
|
|
|
|
AT_DATA([[experr]],
|
|
[[Test module loaded.
|
|
|
|
TRUE
|
|
Test module unloaded.
|
|
Test module loaded.
|
|
|
|
TRUE
|
|
m4:input.m4:6: cannot load symbol `no_such' from module `modtest'
|
|
m4:input.m4:7: cannot open module `no_such'
|
|
]])
|
|
|
|
ls "$abs_builddir"
|
|
|
|
AT_CHECK_M4([-I "$abs_builddir" load input.m4],
|
|
[1], [expout], [experr])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ------------------- ##
|
|
## text module symbols ##
|
|
## ------------------- ##
|
|
|
|
# Support text macros with requested numbers of parameters.
|
|
|
|
AT_SETUP([modules: text])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
AT_DATA([input.m4],
|
|
[[__test__
|
|
__test__(1)
|
|
__test__(1,2)
|
|
onearg
|
|
onearg(1)
|
|
onearg(1,2)
|
|
manyargs
|
|
manyargs(1)
|
|
manyargs(1,2)
|
|
]])
|
|
|
|
AT_CHECK_M4([-I "$abs_builddir" modtest input.m4], [0],
|
|
[[modtest
|
|
modtest
|
|
modtest
|
|
|
|
1
|
|
1
|
|
|
|
1
|
|
1,2
|
|
]], [[Test module loaded.
|
|
m4:input.m4:2: Warning: __test__: extra arguments ignored: 1 > 0
|
|
m4:input.m4:3: Warning: __test__: extra arguments ignored: 2 > 0
|
|
m4:input.m4:4: Warning: onearg: too few arguments: 0 < 1
|
|
m4:input.m4:6: Warning: onearg: extra arguments ignored: 2 > 1
|
|
Test module unloaded.
|
|
]])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
## -------------------- ##
|
|
## trace module symbols ##
|
|
## -------------------- ##
|
|
|
|
# The trace bit should not be lost if a builtin is unloaded from
|
|
# memory and then redefined by a subsequent load.
|
|
|
|
AT_SETUP([modules: trace])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
AT_DATA([[input.m4]],
|
|
[[test
|
|
include(`shadow')
|
|
test
|
|
unload(`shadow')
|
|
test
|
|
include(`shadow')
|
|
test
|
|
]])
|
|
|
|
AT_DATA([[expout]],
|
|
[[test
|
|
Shadow module loaded.
|
|
Shadow::test called.
|
|
|
|
test
|
|
Shadow module loaded.
|
|
Shadow::test called.
|
|
]])
|
|
|
|
AT_DATA([[experr]],
|
|
[[m4trace: -1- test -> `Shadow::`test' called.'
|
|
m4trace: -1- test -> `Shadow::`test' called.'
|
|
]])
|
|
|
|
|
|
AT_CHECK_M4([-I "$abs_builddir" load -t test input.m4],
|
|
[0], [expout], [experr])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
## ----------------- ##
|
|
## unload gnu module ##
|
|
## ----------------- ##
|
|
|
|
AT_SETUP([unload gnu module])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
dnl Ensure that the gnu module does not leak memory. I don't know how
|
|
dnl to portably artificially limit the heap to cause an out-of-memory
|
|
dnl condition in the case of a leak, but examining the run of this test
|
|
dnl in a debugger can show whether it is leaking.
|
|
AT_DATA([input.m4], [[divert(-1)
|
|
define(`forloop',
|
|
`pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
|
|
define(`_forloop',
|
|
`$4`'ifelse($1, `$3', `',
|
|
`define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')
|
|
forloop(`i', `1', `5000', `unload(`gnu')include(`gnu')regexp(`123', `\(4\)?2')')
|
|
]])
|
|
|
|
AT_CHECK_M4([load input.m4], [0])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
## ------------------ ##
|
|
## unload load module ##
|
|
## ------------------ ##
|
|
|
|
AT_SETUP([unload load module])
|
|
AT_CHECK_DYNAMIC_MODULE
|
|
|
|
dnl Ensure that the load module can be unloaded and reloaded (obviously,
|
|
dnl it can't reload itself; the reload occurs on the command line). Since
|
|
dnl the module must be resident (after all, the `unload' builtin had better
|
|
dnl not unmap the memory for the code it is currently executing), make sure
|
|
dnl that resident modules are handled gracefully.
|
|
AT_DATA([in1.m4], [[__load__ 1
|
|
unload(`load') 2
|
|
__load__ 3
|
|
]])
|
|
|
|
AT_DATA([in2.m4], [[__load__ 4
|
|
include(`load') 5
|
|
__load__ 6
|
|
unload(`load') 7
|
|
__load__ 8
|
|
unload(`load') 9
|
|
__load__ 10
|
|
]])
|
|
|
|
AT_CHECK_M4([load in1.m4 load in2.m4], [0],
|
|
[[ 1
|
|
2
|
|
__load__ 3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
__load__ 10
|
|
]])
|
|
|
|
AT_CLEANUP
|