signalapp / libsignal

Home to the Signal Protocol as well as other cryptographic primitives which make Signal possible.
GNU Affero General Public License v3.0
3.63k stars 420 forks source link

Building fails with "This file contains proto3 optional fields, but --experimental_allow_proto3_optional was not set" #523

Closed JsBergbau closed 1 year ago

JsBergbau commented 1 year ago

When trying to build with ./build_jni.sh desktop build process fails with

Compiling libsignal-protocol v0.1.0 (/home/pi/Apps/libsignal-client/rust/protocol)
error: failed to run custom build command for `libsignal-protocol v0.1.0 (/home/pi/Apps/libsignal-client/rust/protocol)`

Caused by:
  process didn't exit successfully: `/home/pi/Apps/libsignal-client/target/release/build/libsignal-protocol-b8c4717a42f20850/build-script-build` (exit status: 101)
  --- stderr
  thread 'main' panicked at 'Protobufs in src are valid: Custom { kind: Other, error: "protoc failed: proto/storage.proto: This file contains proto3 optional fields, but --experimental_allow_proto3_optional was not set.\n" }', rust/protocol/build.rs:14:52
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

using commit a196fb8 but also the same problem with the commit from a few days ago.

jrose-signal commented 1 year ago

Hm, things are working fine for me with protoc 3.21.12. What version of protoc do you have installed?

JsBergbau commented 1 year ago

Thanks for the fast reply. 3.12.4 is installed via sudoapt install protobuf-compiler on Debian 11. Looks like I have to do a manual install as described here https://grpc.io/docs/protoc-installation/

JsBergbau commented 1 year ago

Or where can I add the --experimental_allow_proto3_optional option in the build script?

jrose-signal commented 1 year ago

You know, our Dockerized Java build is using a Debian 10 image, and doesn't install protobuf-compiler. If you unset PROTOC in your environment, will it use the version bundled with prost-build?

(This workaround won't work forever; newer versions of prost-build do not include a bundled protobuf-compiler, and we will eventually update to them.)

JsBergbau commented 1 year ago

There is no PROTOC environment variable set. I had to install it via sudoapt install protobuf-compiler, so it didn't seem to use the bundled version. When uninstalling protobuf-compiler I get, as expected

thread 'main' panicked at 'Protobufs in src are valid: Custom { kind: NotFound, error: "failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): No such file or directory (os error 2)" }', rust/protocol/build.rs:14:52

For better understanding: I'm only trying to compile libsignal for signal-cli as explained here https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal

I am on armv6 (Raspberry PI Zero W) and I didn't find a prebuild library for armv6 so I try to compile myself. This had worked more than one year ago, but since some time I am not receiving messages any more probably because of the outdated library, so I am now trying to update it.

jrose-signal commented 1 year ago

Obligatory "we don't officially support use outside of Signal's official clients", but then I'll note that libsignal is generally not perfectly compatible between releases (that's what the leading "0" in the version is supposed to indicate), and so compiling a newer version than what signal-cli expects will probably lead to crashes.

I think ultimately it is not a major goal of libsignal to remain compatible with older development tools than what the organization uses to build the Signal apps, and so installing protobuf from source is your best bet. Or cross-compiling from a newer system.

JsBergbau commented 1 year ago

Installation from source is also not an easy task, since for protoc you need bazel to build and that makes also a lot of trouble, so: Can you give me a hint what has to be done to crosscompile libsignal? I mean I have crosscompiled before and signal-cli/libsignal are the cause that there is now a JDK17 available for Raspberry PI Zero (W) / armv6 https://github.com/raspberrypi/linux/issues/3306 So I just need some point where to start. Your help is really appreciated.

jrose-signal commented 1 year ago

I found some general instructions for cross-compiling Rust for RasPi systems that look correct to me; the target name is libsignal-jni, which you can see in build_jni.sh.

I'm going to close this issue because I don't think there's any action for Signal to take, but I'll answer questions a bit longer too. Like I said, though, I strongly suspect you'll get to the end of this and then find out that it doesn't work the current version of signal-cli.

JsBergbau commented 1 year ago

Obligatory "we don't officially support use outside of Signal's official clients", but then I'll note that libsignal is generally not perfectly compatible between releases (that's what the leading "0" in the version is supposed to indicate), and so compiling a newer version than what signal-cli expects will probably lead to crashes.

signal-cli defines exactly which version of libsignal is needed for the release, see https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal#determine-the-required-libsignal-client-version So no problem with this.

So I've finally managed to compile and cross-compile libsignal and I want to share my experiences.

Without crosscompiling is the easiest way to

cd /usr/bin/
sudo bash
mv protoc protoc-bin

Then create file protoc

#!/bin/bash

/usr/bin/protoc-bin "$@" --experimental_allow_proto3_optional

This just invokes protoc with the desired --experimental_allow_proto3_optional option. Stdin so piping is not working, have a look here of how to do it (just few lines more code) https://github.com/JsBergbau/nullmailer-Rewrite-Wrapper Then chmod +x protoc compiling now finally works.

Now for cross-compiling. This is a lot more complicated. The following steps may not be 100 % accurate but definitly helpful for others trying the same. Thanks for providing your link for crosscompiling Rust for RasPi systems. That helped me a lot. Since I've already compiled a crosscompiler for building JDK for Raspberry Pi Zero (W) I've used it https://github.com/JsBergbau/GCC-cross-compiler-for-Raspberry-PI-Zero-W

sudo apt-get install gcc-multilib libprotobuf-dev protobuf-compiler cmake clang
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add arm-unknown-linux-gnueabihf
wget https://github.com/JsBergbau/GCC-cross-compiler-for-Raspberry-PI-Zero-W/releases/download/cross-pi-gcc_gcc-10.1_glibc-2.28_binutils-2.31_Buster/cross-pi-gcc_gcc-10.1_glibc-2.28_binutils-2.31_Buster.tar.zst
tar xf cross-pi-gcc_gcc-10.1_glibc-2.28_binutils-2.31_Buster/cross-pi-gcc_gcc-10.1_glibc-2.28_binutils-2.31_Buster.tar.zst
sudo mv cross-pi-gcc /opt

The build_jni.sh has to be modified

if [ "$1" = 'desktop' ];
then
    export PATH="/opt/cross-pi-gcc/bin:$PATH"
    export CROSS_COMPILE=arm-linux-gnueabihf
    export CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=/opt/cross-pi-gcc/bin/arm-linux-gnueabihf-gcc
    export CC=/opt/cross-pi-gcc/bin/arm-linux-gnueabihf-gcc

    echo_then_run cargo build -p libsignal-jni --release --target arm-unknown-linux-gnueabihf
    copy_built_library target/release signal_jni "${DESKTOP_LIB_DIR}/"
elif [ "$1" = 'android' ];

I am not sure if these exports are really necessary, however they work.

Start building with ./build_jni.sh desktop and libsignal_jni.so will be successfully created and available in libsignal-client/target/arm-unknown-linux-gnueabihf/release/

The following error message is just for reference, so that other having trouble with crosscompiling rust will find their way here with the error message. The trick are the three environment variables

    export CROSS_COMPILE=arm-linux-gnueabihf
    export CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=/opt/cross-pi-gcc/bin/arm-linux-gnueabihf-gcc
    export CC=/opt/cross-pi-gcc/bin/arm-linux-gnueabihf-gcc

Now the trickiest part. boring-sys fails to compile, because it tries to use the normal cc and c++. Error message (note the username of the x86 build system is also pi):

error: failed to run custom build command for `boring-sys v2.1.0 (https://github.com/signalapp/boring?branch=libsignal#bdf9d2ac)`

Caused by:
  process didn't exit successfully: `/home/pi/libsignal-client/target/release/build/boring-sys-47d821133f93ac8a/build-script-build` (exit status: 101)
  --- stdout
  cargo:rerun-if-env-changed=BORING_BSSL_PATH
  CMAKE_GENERATOR_arm-unknown-linux-gnueabihf = None
  CMAKE_GENERATOR_arm_unknown_linux_gnueabihf = None
  TARGET_CMAKE_GENERATOR = None
  CMAKE_GENERATOR = None
  CMAKE_PREFIX_PATH_arm-unknown-linux-gnueabihf = None
  CMAKE_PREFIX_PATH_arm_unknown_linux_gnueabihf = None
  TARGET_CMAKE_PREFIX_PATH = None
  CMAKE_PREFIX_PATH = None
  CMAKE_arm-unknown-linux-gnueabihf = None
  CMAKE_arm_unknown_linux_gnueabihf = None
  TARGET_CMAKE = None
  CMAKE = None
  running: "cmake" "/home/pi/.cargo/git/checkouts/boring-b37daebd62069023/bdf9d2a/boring-sys/deps/boringssl" "-DCMAKE_TOOLCHAIN_FILE=/home/pi/.cargo/git/checkouts/boring-b37daebd62069023/bdf9d2a/boring-sys/cmake/armv7-linux.cmake" "-DCMAKE_INSTALL_PREFIX=/home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/build/boring-sys-a9c215cff1764e8f/out" "-DCMAKE_C_FLAGS= -ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfpu=vfp" "-DCMAKE_CXX_FLAGS= -ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfpu=vfp" "-DCMAKE_ASM_FLAGS= -ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfpu=vfp" "-DCMAKE_BUILD_TYPE=MinSizeRel"
  -- The C compiler identification is GNU 10.2.1
  -- The CXX compiler identification is GNU 10.1.0
  -- Detecting C compiler ABI info
  -- Detecting C compiler ABI info - failed
  -- Check for working C compiler: /usr/bin/cc
  -- Check for working C compiler: /usr/bin/cc - broken
  -- Configuring incomplete, errors occurred!
  See also "/home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/build/boring-sys-a9c215cff1764e8f/out/build/CMakeFiles/CMakeOutput.log".
  See also "/home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/build/boring-sys-a9c215cff1764e8f/out/build/CMakeFiles/CMakeError.log".

  --- stderr
  CMake Error at /usr/share/cmake-3.18/Modules/CMakeTestCCompiler.cmake:66 (message):
    The C compiler

      "/usr/bin/cc"

    is not able to compile a simple test program.

    It fails with the following output:

      Change Dir: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/build/boring-sys-a9c215cff1764e8f/out/build/CMakeFiles/CMakeTmp

      Run Build Command(s):/usr/bin/gmake cmTC_c890f/fast && /usr/bin/gmake  -f CMakeFiles/cmTC_c890f.dir/build.make CMakeFiles/cmTC_c890f.dir/build
      gmake[1]: Verzeichnis „/home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/build/boring-sys-a9c215cff1764e8f/out/build/CMakeFiles/CMakeTmp“ wird betreten
      Building C object CMakeFiles/cmTC_c890f.dir/testCCompiler.c.o
      /usr/bin/cc   -ffunction-sections -fdata-sections -fPIC -march=armv6 -marm -mfpu=vfp  -o CMakeFiles/cmTC_c890f.dir/testCCompiler.c.o -c /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/build/boring-sys-a9c215cff1764e8f/out/build/CMakeFiles/CMakeTmp/testCCompiler.c
      cc: error: unrecognized command-line option ‘-marm’; did you mean ‘-mabm’?
      cc: error: unrecognized command-line option ‘-mfpu=vfp’
      gmake[1]: *** [CMakeFiles/cmTC_c890f.dir/build.make:85: CMakeFiles/cmTC_c890f.dir/testCCompiler.c.o] Fehler 1
      gmake[1]: Verzeichnis „/home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/build/boring-sys-a9c215cff1764e8f/out/build/CMakeFiles/CMakeTmp“ wird verlassen
      gmake: *** [Makefile:140: cmTC_c890f/fast] Fehler 2

We have now to force the use of the crosscompiler: This step must be done after compiling fails with an error.

cd /usr/bin sudo mv cc cc.old sudo ln -s /opt/cross-pi-gcc/bin/arm-linux-gnueabihf-gcc cc sudo mv c++ c++.old sudo ln -s /opt/cross-pi-gcc/bin/arm-linux-gnueabihf-c++ c++

Re-Start building with ./build_jni.sh desktop

It will now finish compiling libsignal_jni.so

Now revert the systemchanges

cd /usr/bin sudo bash

just move the symlinks in case you need them again

mv cc cc.ln mv c++ cc++.ln mv cc.old cc mv c++.old c++

jrose-signal commented 1 year ago

Clever solution for protoc, heh.

You should be able to set CC=/opt/cross-pi-gcc/bin/arm-linux-gnueabihf-gcc rather than using symlinks; you can see the Android build in build_jni.sh doing something like this.

JsBergbau commented 1 year ago

Thanks for the hint with CC=/opt/cross-pi-gcc/bin/arm-linux-gnueabihf-gcc that is definitely a better solution. I didn't like the symlink solution neither, at least I was glad to get it compiled. Still with the CC env variable set it seems it requires another addition, because now it fails with linking

Compiling libsignal-jni v0.23.1 (/home/pi/libsignal-client/rust/bridge/jni)
error: linking with `cc` failed: exit status: 1

[a lot of line with the args of cc omitted]

  = note: /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: relocations in generic ELF (EM: 40)
          /usr/bin/ld: /home/pi/libsignal-client/target/arm-unknown-linux-gnueabihf/release/deps/signal_jni.3la2dkqjlmz1h0v6.rcgu.o: error adding symbols: file in wrong format
          collect2: error: ld returned 1 exit status

error: could not compile `libsignal-jni` due to previous error
jrose-signal commented 1 year ago

Ah, your CARGO_TARGET_ARM_LINUX_GNUEABIHF_LINKER should be CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER

JsBergbau commented 1 year ago

Thank you very much. It works now and I've edited the post above. Where did you find the name of the env variable CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER?

I was searching for documentation but did only find here https://doc.rust-lang.org/cargo/reference/environment-variables.html

CARGO_TARGET_<triple>_LINKER — The linker to use, see [target.<triple>.linker](https://doc.rust-lang.org/cargo/reference/config.html#targettriplelinker). The triple must be [converted to uppercase and underscores](https://doc.rust-lang.org/cargo/reference/config.html#environment-variables).

and https://doc.rust-lang.org/cargo/reference/config.html#targettriplelinker

[target.<triple>.linker](https://doc.rust-lang.org/cargo/reference/config.html#targettriplelinker)

    Type: string (program path)
    Default: none
    Environment: CARGO_TARGET_<triple>_LINKER

Specifies the linker which is passed to rustc (via [-C linker](https://doc.rust-lang.org/rustc/codegen-options/index.html#linker)) when the [<triple>](https://doc.rust-lang.org/cargo/appendix/glossary.html#target) is being compiled for. By default, the linker is not overridden.

To be exact this is no triple anymore, but a quadruple ;)

jrose-signal commented 1 year ago

That's the correct documentation, yes. The "triple" comes from the Rust platform target name (from this big list), and yeah, it hasn't been a "triple" for a long time. (Coincidentally, there was just a thread on the Swift forums about this.)

JsBergbau commented 11 months ago

Crosscompiling boring-sys is still an issue. The workaround with symlink is still needed. I've added the steps again https://github.com/signalapp/libsignal/issues/523#issuecomment-1562970981