This PR prevents symbol clashes between (non-`@[export]`) definitions from different Lean packages. Previously, if two modules define a function with the same name and were transitively imported (even privately) by some downstream module, linking would fail due to a symbol clash. Similarly, if a user defined a symbol with the same name as one in the `Lean` library, Lean would use the core symbol even if one did not import `Lean`. This is solved by changing Lean's name mangling algorithm to include an optional package identifier. This identifier is provided by Lake via `--setup` when building a module. This information is weaved through the elaborator, interpreter, and compiler via a persistent environment extension that associates modules with their package identifier. With a package identifier, standard symbols have the form `lp_<pkg-id>_<mangled-def>`. Without one, the old scheme is used (i.e., `l_<mangled-def>`). Module initializers are also prefixed with package identifier (if any). For example, the initializer for a module `Foo` in a package `test` is now `initialize_test_Foo` (instead of `initialize_Foo`). Lake's default for native library names has also been adjusted accordingly, so that libraries can still, by default, be used as plugins. Thus, the default library name of the `lean_lib Foo` in `package test` will now be `libtest_Foo`. When using Lake to build the Lean core (i.e., `bootstrap = true`), no package identifier will be used. Thus, definitions in user packages can never have symbol clashes with core. Closes #222.
59 lines
1.6 KiB
Bash
Executable file
59 lines
1.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Deermine shared library extension
|
|
if [ "${OS:-}" = Windows_NT ]; then
|
|
LIB_PREFIX=
|
|
SHLIB_EXT=dll
|
|
elif [ "`uname`" = Darwin ]; then
|
|
LIB_PREFIX=lib
|
|
SHLIB_EXT=dylib
|
|
else
|
|
LIB_PREFIX=lib
|
|
SHLIB_EXT=so
|
|
fi
|
|
|
|
# Reset test
|
|
./clean.sh
|
|
lake update -q
|
|
|
|
# Build plugins
|
|
lake build
|
|
PKG=user__plugin # mangled
|
|
LIB_DIR=.lake/build/lib
|
|
check_plugin () {
|
|
plugin=$1
|
|
shlib=$LIB_DIR/${LIB_PREFIX}${PKG}_$plugin.$SHLIB_EXT
|
|
test -f $shlib || {
|
|
echo "$plugin library not found; $LIB_DIR contains:"
|
|
ls $LIB_DIR
|
|
exit 1
|
|
}
|
|
}
|
|
check_plugin UserPlugin
|
|
check_plugin UserEnvPlugin
|
|
PLUGIN=$LIB_DIR/${LIB_PREFIX}${PKG}_UserPlugin.$SHLIB_EXT
|
|
ENV_PLUGIN=$LIB_DIR/${LIB_PREFIX}${PKG}_UserEnvPlugin.$SHLIB_EXT
|
|
|
|
# Expected test output
|
|
EXPECTED_OUT="Ran builtin initializer"
|
|
ENV_EXPECTED_OUT="Builtin value"
|
|
|
|
# Test plugins at elaboration-time via `lean` CLI
|
|
echo "Testing plugin load with lean CLI ..."
|
|
echo | lean --plugin=$PLUGIN --stdin 2>&1 | diff <(echo "$EXPECTED_OUT") -
|
|
lake env lean --plugin=$ENV_PLUGIN testEnvUse.lean 2>&1 | diff <(echo "$ENV_EXPECTED_OUT") -
|
|
|
|
# Test plugins at runtime via `Lean.loadPlugin`
|
|
echo "Testing plugin load with Lean.loadPlugin ..."
|
|
lean --run test.lean $PLUGIN 2>&1 | diff <(echo "$EXPECTED_OUT") -
|
|
lake env lean --run testEnv.lean $ENV_PLUGIN 2>&1 | diff <(echo "$ENV_EXPECTED_OUT") -
|
|
|
|
# Test failure to load environment plugin without `withImporting`
|
|
lean --run test.lean $ENV_PLUGIN >/dev/null 2>&1 && {
|
|
echo "Loading environment plugin without importing succeeded unexpectedly."
|
|
exit 1
|
|
} || true
|
|
|
|
# Print success
|
|
echo "Tests completed successfully."
|