How to use a core JUCE module in Google Cloud Run C++ service

I’m trying to deploy a C++ service on Google Cloud Run which uses some JUCE modules (e.g. juce_core, juce_cryptography, etc). I’m following this guide very closely: Quickstart: Deploy a C++ service to Cloud Run  |  Cloud Run Documentation  |  Google Cloud

So far, I setup the project as per the guide and got things running. Then, I added JUCE as a submodule inside the project folder, and tried adding JUCE to CMakeLists.txt using target_link_libraries (shown below). However, I am getting errors while building the juceaide:

"ft2build.h: No such file or directory"
"X11/Xlib.h: No such file or directory"

Also, this error happens in a few places:

/v/source/Modules/juce/modules/juce_core/native/juce_linux_SystemStats.cpp:216:28:
#12 415.2   error: '_NL_IDENTIFICATION_TERRITORY' was not declared in this scope

To solve this issue, I’m wondering if I need to use juce_add_console_app in CMakeLists.txt, or make any other changes there?

Another thought: some Linux dependencies may be missing, so I’ve tried adding a few libraries mentioned in the JUCE docs Linux dependencies (see under Dockerfile line RUN apk update what I added). Google Cloud Run Linux distro is Alpine, not Ubuntu, so seem to be some differences in package names.

Finally, I could also try disabling some dependencies I don’t need in CMakeLists.txt using

target_compile_definitions (...
    PUBLIC
        JUCE_WEB_BROWSER=0
        JUCE_USE_CURL=0
etc

Anyway, any ideas or suggestions appreciated! Below are the referenced source files:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)

project(cpp-samples-cloud-run-hello-world CXX)

add_subdirectory(Modules/juce)

# I added this:
set(JUCE_DEPENDENCIES
    juce::juce_core
    juce::juce_cryptography
    juce::juce_data_structures
    juce::juce_events
    juce::product_unlocking
        )

find_package(functions_framework_cpp REQUIRED)
find_package(Threads)

add_executable(cloud_run_hello cloud_run_hello.cc)
target_compile_features(cloud_run_hello PRIVATE cxx_std_17)
target_link_libraries(cloud_run_hello 
    functions-framework-cpp::framework
# I added this:
    PRIVATE
    ${JUCE_DEPENDENCIES}
    PUBLIC
    juce::juce_recommended_config_flags
    juce::juce_recommended_lto_flags
    juce::juce_recommended_warning_flags
    )

include(GNUInstallDirs)
install(TARGETS cloud_run_hello RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

Dockerfile:

# We chose Alpine to build the image because it has good support for creating
# statically-linked, small programs.
FROM alpine:3.16 AS base

# Create separate targets for each phase, this allows us to cache intermediate
# stages when using Google Cloud Build, and makes the final deployment stage
# small as it contains only what is needed.
FROM base AS devtools

# Install the typical development tools for C++, and
# the base OS headers and libraries.
RUN apk update && \
    apk add \
        build-base \
        cmake \
        curl \
        git \
        gcc \
        g++ \
        libc-dev \
        linux-headers \
        ninja \
        pkgconfig \
        tar \
        unzip \
        zip \
# I added these so far:
        libexecinfo-dev \ 
        libexecinfo \
        freetype

# Use `vcpkg`, a package manager for C++, to install 
WORKDIR /usr/local/vcpkg
ENV VCPKG_FORCE_SYSTEM_BINARIES=1
RUN curl -sSL "https://github.com/Microsoft/vcpkg/archive/2022.07.25.tar.gz" | \
    tar --strip-components=1 -zxf - \
    && ./bootstrap-vcpkg.sh -disableMetrics

# Copy the source code to /v/source and compile it.
FROM devtools AS build
COPY . /v/source
WORKDIR /v/source

# Run the CMake configuration step, setting the options to create
# a statically linked C++ program
RUN cmake -S/v/source -B/v/binary -GNinja \
    -DCMAKE_TOOLCHAIN_FILE=/usr/local/vcpkg/scripts/buildsystems/vcpkg.cmake \
    -DCMAKE_BUILD_TYPE=Release

# Compile the binary and strip it to reduce its size.
RUN cmake --build /v/binary
RUN strip /v/binary/cloud_run_hello

# Create the final deployment image, using `scratch` (the empty Docker image)
# as the starting point. Effectively we create an image that only contains
# our program.
FROM scratch AS cloud-run-hello
WORKDIR /r

# Copy the program from the previously created stage and the shared libraries it
# depends on.
COPY --from=build /v/binary/cloud_run_hello /r
COPY --from=build /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1
COPY --from=build /usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so.6
COPY --from=build /usr/lib/libgcc_s.so.1 /usr/lib/libgcc_s.so.1

# Make the program the entry point.
ENTRYPOINT [ "/r/cloud_run_hello" ]

Maybe that will help:

Here’s the basics for the dockerfile I’m using on Google Cloud Run with JUCE:

And here’s a template project configured with that (prebuilt) docker image:

3 Likes

I copied your JuceDocker project content into my Cloud Run project to test this (replacing all my content, except for keeping my project’s name in CMakeLists.txt).

Unfortunately, when running the Cloud Run project locally, I get this error after building/exporting juceaide:

-- Building juceaide
-- Exporting juceaide
-- CPM: adding package JUCE_Config@ (origin/master)
By not providing "Findjuce.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "juce", but
CMake did not find one.
Could not find a package configuration file provided by "juce" with any of
the following names:
   juceConfig.cmake
   juce-config.cmake

Add the installation prefix of "juce" to CMAKE_PREFIX_PATH or set
"juce_DIR" to a directory containing one of the above files.

I’m not sure what’s causing this. To try to fix, I re-added JUCE to the project as a local submodule, then added these:

  • added -DCPM_JUCE_SOURCE=Modules/JUCE to the RUN cmake -G Ninja -B build ... command in Dockerfile
  • added add_subdirectory(Modules/juce JUCE) in CMakeLists.txt
  • added set(CMAKE_PREFIX_PATH Modules/juce/extras/Build/CMake/)
    No success with these yet.

Let me know if anything comes to mind about this error during CPM: adding package JUCE_Config@.
Thanks for the help!

Does it ever build with musl on Alpine? I mean, did you try in your local/VM env?

Sorry about that, fixed the build now.

I pulled down your updates to JUCEDocker, but am still getting same issue when deploying it as a Cloud Run service.

Here’s a more complete stack trace. Sorry if this is something super obvious!

11 0.470 -- Check for working CXX compiler: /usr/bin/c++
11 0.547 -- Check for working CXX compiler: /usr/bin/c++ -- works
11 0.548 -- Detecting CXX compiler ABI info
11 0.625 -- Detecting CXX compiler ABI info - done
11 0.633 -- Detecting CXX compile features
11 0.633 -- Detecting CXX compile features - done
11 0.636 -- CPM: adding package JUCE_Config@ (origin/master)
11 1.622 CMake Error at build/_deps/juce_config-src/CMakeLists.txt:33 (find_package):
11 1.622   By not providing "Findjuce.cmake" in CMAKE_MODULE_PATH this project has
11 1.622   asked CMake to find a package configuration file provided by "juce", but
11 1.622   CMake did not find one.
11 1.622 
11 1.622   Could not find a package configuration file provided by "juce" with any of
11 1.622   the following names:
11 1.622 
11 1.622     juceConfig.cmake
11 1.622     juce-config.cmake
11 1.622 
11 1.622   Add the installation prefix of "juce" to CMAKE_PREFIX_PATH or set
11 1.622   "juce_DIR" to a directory containing one of the above files.  If "juce"
11 1.622   provides a separate development package or SDK, be sure it has been
11 1.622   installed.

Woops, looks like I still had some local changes in there, committed them all now.

You should be able to test it locally before deploying by doing:

docker build . -t jucedocker:latest
docker run jucedocker:latest
1 Like

Much appreciated! After I pulled the latest updates, those docker commands now build and run the container locally.

I’ve also been able to deploy to Cloud Run. However there is an error in the container after the Debug messages print out there - my app is expecting the program to listen on port 8080, but it currently just prints out the debug message.

To fix this, I’m trying to add the Google Cloud Functions Framework Cpp to the project, to listen on the port as they do in the Google Cloud Run C++ example:

So I tried adding the functions-framework-cpp library using find_package(functions_framework_cpp REQUIRED) and then linking it via target_link_libraries(${TargetName} functions-framework-cpp::framework but now getting similar error to before:

Could not find a package configuration file provided by
"functions_framework_cpp" with any of the following names:
functions_framework_cppConfig.cmake
functions_framework_cpp-config.cmake

Maybe I need to install something more in the Dockerfile… Will update if I make any progress on this. Thanks again for the help @eyalamir

I’ve never tried the google framework, so no idea, but this looks like a build error that you can fix locally.

In my experience, any kind of “regular” http frameworks work. For example I use the extremely easy to use and build cpp-httplib

And listen on port 8080. Notice that you only need to listen to http (and not https) as Google Cloud Run will provide the https connection and then locally forward it to your http port.

1 Like

Thanks for the suggestion @eyalamir! that HTTP library worked well for this use-case.

Here’s the basic implementation for anyone curious -

#include "httplib.h"
... any juce includes you need...

int main()
{   
    httplib::Server svr;
    svr.Get("/",
        [](const httplib::Request& req, httplib::Response& res) {
           ...
           // set the response content
          res.set_content("Hello World!", "text/plain");
        }
    );
    svr.listen("0.0.0.0", 8080);
    return 0;
}

Another note - a few Juce dependencies I added (juce_gui_basics I believe, maybe some others) were looking for libfreetype6, so I needed to add that to the Dockerfile on line 16:

1 Like