Zulip Chat Archive

Stream: lean4

Topic: changes to lake and FFI


Tomas Skrivan (May 29 2025 at 11:10):

With recent changes to lake I'm unable to get my FFI setup working again.

To demonstrate the problem I have set up a simple project which has:

FFI/Basic.lean

def hello := "world"

FFI/Compiled.lean

@[extern "print_usize"]
opaque printUSize (n : USize) : IO Unit

FFI.lean

import FFI.Basic
import FFI.Compiled

Main.lean

import FFI

def main : IO Unit := do
  IO.println s!"Hello, {hello}!"
  printUSize 42

#eval main

lakefile.lean (inspired by lake ffi example)

lakefile.lean

I have few issues I do not know how to resolve:

  1. lake build has no problem evaluating #eval main but infoview in VS Code and Emacs hangs on it.
  2. How can I change the setup such that FFI/Basic.lean is not compiled but FFI/Compiled.lean is compiled? The reason for doing this is that FFI/Basic.lean imports large part of mathlib which I do not want to compile.

Previously I just replaced

lean_lib FFI where
  roots := #[`FFI]
  precompileModules := true
  moreLinkObjs := #[libleanffi]

with

lean_lib FFI

lean_lib FFICompiled where
  roots := #[`FFI.Compiled]
  precompileModules := true
  moreLinkObjs := #[libleanffi]

but now this fails with

info: stderr:
libc++abi: terminating due to uncaught exception of type lean::exception: error loading plugin, initializer not found 'initialize_FFICompiled'
error: Lean exited with code 134
Some required builds logged failures:
- FFI

Kim Morrison (May 29 2025 at 11:30):

@Mac Malone

Mac Malone (May 29 2025 at 15:16):

@Tomas Skrivan FFICompiled should be

lean_lib FFI.Compiled where
  precompileModules := true
  moreLinkObjs := #[libleanffi]

Tomas Skrivan (May 29 2025 at 15:18):

What if I have 20 lean files I want to compile? Do I have to make one lean_lib for each?

Tomas Skrivan (May 29 2025 at 15:22):

Why is the name of lean_lib important? It didn't use to be. The files to compile were specified through the roots field.

Tomas Skrivan (May 29 2025 at 15:30):

Ok, I can turn FFI.Compiled.lean into a folder and all the files in it will be compiled. I'm still confused about the lean_lib naming and the roots field.

Mac Malone (May 29 2025 at 15:31):

Tomas Skrivan said:

What if I have 20 lean files I want to compile? Do I have to make one lean_lib for each?

The standard approach would to have a single precompiled root module which imports the 20.

Tomas Skrivan said:

Why is the name of lean_lib important? It didn't use to be. The files to compile were specified through the roots field.

Lake now tries to load the library as plugin if it can (i.e., via lean --plugin instead of lean --load-dynlib). This requires the root and the library to be named in a speciifc way and Lake's heuristic for detecting this is currently inaccurate. lean4#8528 will fix this.

Tomas Skrivan (May 29 2025 at 16:52):

Why does the order of lean_lib matter?

This works:

lean_lib FFI

lean_lib FFI.Compiled where
  precompileModules := true
  moreLinkObjs := #[libleanffi]

but this does not:

lean_lib FFI.Compiled where
  precompileModules := true
  moreLinkObjs := #[libleanffi]

lean_lib FFI

Why is that? I find it quite confusing as there has to be some dependency between the two that is not explicitly written.

Mac Malone (May 29 2025 at 17:21):

Tomas Skrivan said:

Why does the order of lean_lib matter?

Later libraries shadow earlier ones. Both FFI and FFI.Compiled match FFI.Compiled. That is, they overlap -- FFI matches any module FFI.* and FFI.Compiled matches any module FFI.Compiled.*. If FFI.Compiled comes later, its configuration is used; otherwise, the configuration of FFI is used.

Tomas Skrivan (May 30 2025 at 08:32):

Thanks! I have it working now on v4.19.0

Tomas Skrivan (May 30 2025 at 08:38):

However, not I'm trying to bump to v4.20.0-rc5 and I'm getting bunch of undefined reference errors. For examples:

ld.lld: error: undefined symbol: l_Array_emptyWithCapacity
>>> referenced by Congr.c
>>>               /home/tskrivan/Documents/LeanBLAS/.lake/packages/batteries/.lake/build/ir/Batteries/Tactic/Congr.c.o.export:(initialize_Batteries_Tactic_Congr)

full error message

Right now I do not know how to minimize it. I'm updating LeanBLAS, this v4.19.0 commit works but this v4.20.0-rc5 commit does not.

Tomas Skrivan (May 30 2025 at 08:49):

Interestingly it fails with a different error on Linux during CI

info: stderr:
ld.lld: error: undefined symbol: __libc_csu_fini
>>> referenced by start.S:101 (../sysdeps/x86_64/start.S:101)
>>>               /home/runner/.elan/toolchains/leanprover--lean4---v4.20.0-rc5/lib/Scrt1.o:(_start)

ld.lld: error: undefined symbol: __libc_csu_init
>>> referenced by start.S:102 (../sysdeps/x86_64/start.S:102)
>>>               /home/runner/.elan/toolchains/leanprover--lean4---v4.20.0-rc5/lib/Scrt1.o:(_start)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: external command '/home/runner/.elan/toolchains/leanprover--lean4---v4.20.0-rc5/bin/clang' exited with code 1
Some required builds logged failures:
- CBLASLevelOneTest
error: build failed

and on MacOS it passes

Tomas Skrivan (May 30 2025 at 10:36):

Now I'm updating SciLean that uses LeanBLAS and I'm getting an odd error

 [1847/1856] Running LeanBLAS.FFI:shared
error: type mismtach in target 'libleanblasc': expected 'filepath', got unknown

where libleanblasc is the target in LeanBLAS that build the C library

target libleanblasc pkg : FilePath := do
    ...
    buildStaticLib (pkg.sharedLibDir / name) (oFiles)

Tomas Skrivan (May 30 2025 at 11:21):

This makes me wonder if there is an issue when having a project with FFI as a dependency.

I try to reproduce it with the simple ffi project.

Mac Malone (May 30 2025 at 20:26):

The first set of errors seems like it may be an issue caused by an incompatibility with the new LLVM toolchain used in Lean v4.20.0. LLVM has been upgraded from 15 to 19. On your local machine, it might be worth clobbering the .lake directory and seeing if that changes the errors to mirror the CI (some aspect of the version update may have not been caught by Lake).

The 'filepath' issue, on the other hand, may more likely be a Lake problem.

Tomas Skrivan (Jun 07 2025 at 14:58):

I can't reproduce the filepath problem with the minimal example but with LeanBLAS

Steps to reproduce on Ubuntu:

$ sudo apt-get install libopenblas-dev
$ mkdir leanblasdep; cd leanblasdep
$ lake init leanblasdep .lean

replace lakefile.lean with:

import Lake
open Lake DSL

package "leanblasdep" where
  version := v!"0.1.0"
  moreLinkArgs := #["-L/usr/lib/x86_64-linux-gnu/", "-lblas", "-lm"]

lean_lib «Leanblasdep» where

@[default_target]
lean_exe "leanblasdep" where
  root := `Main

require leanblas from git "https://github.com/lecopivo/LeanBLAS" @ "v4.20.1"

Now running lake build LeanBLAS.FFI:static produces this error:

$ lake build LeanBLAS.FFI:static
 [13/14] Building leanblas/libleanblasc
trace: .> gcc -c -o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/leveltwoext.o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/c/leveltwoext.c -I /home/tskrivan/.elan/toolchains/leanprover--lean4---v4.21.0-rc3/include -DNDEBUG -O3 -fPIC
trace: .> gcc -c -o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/util.o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/c/util.c -I /home/tskrivan/.elan/toolchains/leanprover--lean4---v4.21.0-rc3/include -DNDEBUG -O3 -fPIC
trace: .> gcc -c -o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/levelone.o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/c/levelone.c -I /home/tskrivan/.elan/toolchains/leanprover--lean4---v4.21.0-rc3/include -DNDEBUG -O3 -fPIC
trace: .> gcc -c -o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/leveltwo.o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/c/leveltwo.c -I /home/tskrivan/.elan/toolchains/leanprover--lean4---v4.21.0-rc3/include -DNDEBUG -O3 -fPIC
trace: .> /home/tskrivan/.elan/toolchains/leanprover--lean4---v4.21.0-rc3/bin/llvm-ar rcs /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/lib/libleanblasc.a /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/leveltwoext.o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/util.o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/levelone.o /home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/c/leveltwo.o
info: stderr:
/home/tskrivan/.elan/toolchains/leanprover--lean4---v4.21.0-rc3/bin/llvm-ar: error: unable to load '/home/tskrivan/doodle/lean/leanblasdep/.lake/packages/leanblas/.lake/build/lib/libleanblasc.a': file too small to be an archive
error: external command '/home/tskrivan/.elan/toolchains/leanprover--lean4---v4.21.0-rc3/bin/llvm-ar' exited with code 1
 [14/14] Running LeanBLAS.FFI:static
error: type mismtach in target 'libleanblasc': expected 'filepath', got unknown
Some required builds logged failures:
- leanblas/libleanblasc
- LeanBLAS.FFI:static
error: build failed

Tomas Skrivan (Jun 08 2025 at 10:26):

I can reproduce with the SimpleFFI project now.

mkdir SimpleFFIDep; cd SimpleFFIDep
lake init simpleffidep .lean
echo 'require ffi from git "https://github.com/lecopivo/SimpleFFI" @ "alt"' >> lakefile.lean
{ echo 'import FFI'; cat Main.lean; } > tmp.lean && mv tmp.lean Main.lean
lake update
lake build

which ends with

$ lake build
✖ [13/23] Running FFI.Compiled:shared
error: type mismtach in target 'libleanffi': expected 'filepath', got unknown
✖ [23/23] Running simpleffidep
error: type mismtach in target 'libleanffi': expected 'filepath', got unknown
Some required builds logged failures:
- FFI.Compiled:shared
- simpleffidep
error: build failed

Tomas Skrivan (Jun 08 2025 at 10:30):

It has to do something with building the *.c files with

code

vs

code

The first version works, the second does not. It is not clear to me how to generalize the first approach if I want to just compile all the *.c files in the c/ directory.

Tomas Skrivan (Jun 08 2025 at 10:34):

Ahh, it has something to do with pkg.afterBuildCacheAsync. It is leftover code when I was experimenting with github releases. Removing it solves the problem with filepath

Tomas Skrivan (Jun 08 2025 at 10:43):

However, I still have the problem executing ffi functions in the editor.

mwe:

git clone git@github.com:lecopivo/SimpleFFI.git
cd SimpleFFI
lake update
lake build

Open Main.lean in VS Code and the orange line does not go away for me. Same happens in emacs.

Arthur Paulino (Sep 18 2025 at 18:33):

Did anything change regarding the memory layout of Lean objects from 4.21 to 4.23? Like the ordering of constructor pointers or anything else?

Our FFI glue code is now segfaulting. We're trying to minimize the problem but any clue would help. Thanks a lot!

Arthur Paulino (Sep 18 2025 at 19:12):

(deleted)


Last updated: Dec 20 2025 at 21:32 UTC