Sohail Si
Sohail Si

Reputation: 2976

Link clang++ modules: Function exported using C++20 modules is not visible (clang++): Cannot compile executable file that uses module

I am exporting a function in the following C++20 module. Although the main program can import the module, but it cannot see the exported function:

f1.hpp

export module f1_module;
export void f1() {
}

f1_demo.cpp

import f1_module;
int main() {
   f1();
   return 0;
}

The build script is:

#!/bin/bash
mkdir -p ./target

FLAGS="-std=c++20 -stdlib=libc++ -fmodules -fbuiltin-module-map"

clang++ $FLAGS \
    -fprebuilt-module-path=./target \
    -Xclang -emit-module-interface  \
    -c \
    f1.hpp \
    -o ./target/f1.module.o

clang++ $FLAGS \
    -fprebuilt-module-path=./target \
        -fmodule-file=f1_module=./target/f1.module.o \
    f1_demo.cpp \
    -o ./target/f1_demo.o

The output error: bash build.bash:

ld: error: undefined symbol: f1()
>>> referenced by f1_demo.cpp
>>>               /tmp/f1_demo-286314.o:(main)
clang-14: error: linker command failed with exit code 1 (use -v to see invocation)

The exported function f1() is not visible in the main program. How to make it work?

I can provide the output to -v.

I am running above build script in conanio/clang14 docker container:

docker run -it --rm -v $(pwd):/sosi conanio/clang14-ubuntu16.04:latest  bash

Update: Cannot link

I tried @DavisHerring 's suggestion, adding -c to the second command, however, it does not generate executable. What I have is a regular object file, and a compiled module file. (The outcome of of -c is not executable). But I need an executable: I cannot link them. When tried to link using clang++, the problem is about linking:

I cannot link an object file with a compiled module file:

clang++ $FLAGS \
    -Xclang -emit-module-interface  \
    -c \
    f1_module.cpp \
    -o ./target/f1_module.o

clang++ $FLAGS \
    -fmodule-file=f1_module=./target/f1_module.o \
    -c \
    f1_demo.cpp \
    -o ./target/f1_demo.o

clang \
    -v \
    ./target/f1_module.o \
    ./target/f1_demo.o \
    -o ./target/exe.o

output:

"/usr/local/bin/ld" -z relro --hash-style=gnu --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o ./target/exe.e /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/local/lib/clang/14.0.0/lib/linux/clang_rt.crtbegin-x86_64.o -L/usr/local/bin/../lib/gcc/x86_64-linux-gnu/10.3.0 -L/usr/local/bin/../lib/gcc/x86_64-linux-gnu/10.3.0/../../../../lib64 -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/local/bin/../lib -L/usr/local/bin/../lib64 -L/lib -L/usr/lib ./target/f1_module.module ./target/f1_demo.o /usr/local/lib/clang/14.0.0/lib/linux/libclang_rt.builtins-x86_64.a --as-needed -l:libllvm-unwind.so --no-as-needed -lc /usr/local/lib/clang/14.0.0/lib/linux/libclang_rt.builtins-x86_64.a --as-needed -l:libllvm-unwind.so --no-as-needed /usr/local/lib/clang/14.0.0/lib/linux/clang_rt.crtend-x86_64.o /usr/lib/x86_64-linux-gnu/crtn.o

followed by error: unclosed quote

ld: error: ./target/f1_module.o:1427: unclosed quote
clang-14: error: linker command failed with exit code 1 (use -v to see invocation)

Note: I am running it on MacBook M1 (arm64) with MacOS Monterey 12.4 and run clang++ via Docker 4.9.1 ( Engine: 20.10.16 ).

Upvotes: 1

Views: 554

Answers (1)

n. m. could be an AI
n. m. could be an AI

Reputation: 119847

You want to compile a module twice. Once to emit a module interface file (which should not have the .o suffix BTW; the customary suffix is .pcm, for "precompiled module") and once to emit an object file (with the customary .o suffix).

clang++ -std=c++20 -c f1_module.cpp -o target/f1_module.o # plain old object
clang++ -std=c++20 -Xclang -emit-module-interface \
      -c f1_module.cpp -o target/f1_module.pcm # module interface

Now your module (consisting of two files) is ready, you can use it.

You need to compile the main file against the .pcm file and link the resulting objects against the .o file to produce an executable.

clang++ -std=c++20 -fprebuilt-module-path=./target \
      -c f1_demo.cpp -o target/f1_demo.o
clang++ -std=c++20 target/f1_demo.o target/f1_module.o -o target/f1.exe

This is not strictly necessary. With clang, the precompiled module can be used as an object file. The linker however won't recognize it, so clang will need to convert .pcm to .o and feed the temporary .o to the linker, each time you link. This is somewhat of a waste so you may want to eliminate this conversion step by building a separate .o file as above. If you choose not to, you still need to mention the module file on the link line. The simplified process is like this:

clang++ -std=c++20 -Xclang -emit-module-interface \
      -c f1_module.cpp -o target/f1_module.pcm # compile module once
clang++ -std=c++20 -fprebuilt-module-path=./target \
      -c f1_demo.cpp -o target/f1_demo.o # compile main just as before
clang++ -std=c++20 target/f1_demo.o target/f1_module.pcm \
      -o target/f1.exe # mention .pcm explicitly 

As far as I know (and I don't know very much), there is currently no way to have clang find out automatically which modules to link against.

Upvotes: 2

Related Questions