Can't build GuiApp with tracktion_engine on raspberry pi

I ported my first project to CMake. I build the app successfully on MacOS. Now I’m trying to build it on Raspberry and getting errors at the very end of the build process:

/usr/include/c++/10/bits/stl_uninitialized.h:84:9: note: parameter passing for argument of type ‘__gnu_cxx::__normal_iterator<const tracktion_engine::RackNodeBuilder::RackConnection*, std::vector<tracktion_engine::RackNodeBuilder::RackConnection> >’ changed in GCC 7.1

/tmp/ccq8JWtV.s: Assembler messages:

/tmp/ccq8JWtV.s:6586: Error: selected processor does not support `yield' in ARM mode

gmake[2]: *** [CMakeFiles/Bento.dir/build.make:177: CMakeFiles/Bento.dir/home/pi/dev/app/BentoCmake/modules/tracktion_engine/tracktion_engine_playback.cpp.o] Fehler 1

gmake[1]: *** [CMakeFiles/Makefile2:193: CMakeFiles/Bento.dir/all] Fehler 2

gmake: *** [Makefile:149: all] Fehler 2

Here is my CMakeList.txt. This is a version from the tracktion_engine with a few edits.

cmake_minimum_required(VERSION 3.15)

project(Bento VERSION 1.0.0)

# If we are using MSVC we want static runtime linkage
if (MSVC)
    set (CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()

# If we are compiling for macOS we want to target OS versions down to 10.11
if (APPLE)
    set (CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE INTERNAL "")
endif()

# Compile with werror in release builds
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR
    CMAKE_CXX_COMPILER_ID MATCHES "GNU")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Werror")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /WX")
endif()


# Adds all the module sources so they appear correctly in the IDE
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
option(JUCE_ENABLE_MODULE_SOURCE_GROUPS "Enable Module Source Groups" ON)

add_subdirectory(../../modules/juce ./cmake_build_juce)
add_subdirectory(../../modules ./cmake_build_tracktion)

if (DEFINED ENV{VST2_SDK_DIR})
    MESSAGE(STATUS "Building with VST2 SDK: $ENV{VST2_SDK_DIR}")
    juce_set_vst2_sdk_path($ENV{VST2_SDK_DIR})
else()
    MESSAGE(STATUS "Not building with VST2")
endif()

set (TargetName ${PROJECT_NAME})

juce_add_gui_app(${TargetName} PRODUCT_NAME "BentoApp")
juce_generate_juce_header(${TargetName})

target_sources(${TargetName} PRIVATE
    Source/Main.cpp
    Source/BentoPitchShiftPlugin.cpp
    Source/BinaryData.cpp
)


# Add all asset files
juce_add_bundle_resources_directory("${PROJECT_NAME}" "Assets")


target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE cxx_std_17)

set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
    C_VISIBILITY_PRESET hidden
    CXX_VISIBILITY_PRESET hidden)



if (DEFINED ENV{VST2_SDK_DIR})
    target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
        JUCE_PLUGINHOST_VST=1)
endif()

target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
    JUCE_PLUGINHOST_AU=1
    JUCE_PLUGINHOST_LADSPA=1
    JUCE_PLUGINHOST_VST3=1
    JUCE_USE_CURL=0
    JUCE_WEB_BROWSER=0
    JUCER_ENABLE_GPL_MODE=1
    JUCE_DISPLAY_SPLASH_SCREEN=0
    JUCE_REPORT_APP_USAGE=0
    JUCE_MODAL_LOOPS_PERMITTED=1
    JUCE_STRICT_REFCOUNTEDPOINTER=1
    TRACKTION_ENABLE_TIMESTRETCH_SOUNDTOUCH=1
    )

target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
    tracktion::tracktion_engine
    tracktion::tracktion_graph
    juce::juce_audio_devices
    juce::juce_audio_processors
    juce::juce_audio_utils
    juce::juce_recommended_warning_flags)

if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas")
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
  target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE "-latomic")
  target_link_options(${CMAKE_PROJECT_NAME} PRIVATE "-m64")
endif()

Raspberry

Hardware: BCM2711
Model: Raspberry Pi 400 Rev 1.0
OS: Raspian (32Bit)

I’m not sure if is a CMake, tracktion_engine and / or Linux issue.

It’s a bit suspicious that you’re passing the -m64 flag to the linker - I’m pretty sure this is intended to be a compiler flag, not a linker flag. On my raspberry pi, the linker does support the -m option, but only with the arguments armelf_linux_eabi or armelfb_linux_eabi.

It would also be a bit suspicious to pass the -m64 compiler flag on a 32-bit OS.

I think you probably shouldn’t pass this flag on Raspberry Pi. Removing it might fix the issue.

Other than that, it looks like similar issues are often fixed by passing -mcpu=native as a compiler flag. If removing -m64 doesn’t help, you could try adding -mcpu=native to CMAKE_CXX_FLAGS.


On a different topic, you can add juce::juce_recommended_warnings_flags to your target_link_libraries call to enable all of the recommended JUCE warnings. This might be more convenient than modifying CMAKE_CXX_FLAGS directly - and it will enable a few more warnings, potentially improving the quality of your code.

1 Like

Thanks! The build process continues with this modification

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
    target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE "-latomic")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native")
endif()

Now I get another error:

In file included from /home/pi/dev/app/BentoCmake/modules/juce/modules/juce_core/juce_core.h:335,
                 from /home/pi/dev/app/BentoCmake/modules/juce/modules/juce_audio_basics/juce_audio_basics.h:54,
                 from /home/pi/dev/app/BentoCmake/modules/juce/modules/juce_audio_formats/juce_audio_formats.h:57,
                 from /home/pi/dev/app/BentoCmake/modules/tracktion_graph/tracktion_graph.cpp:21:
/home/pi/dev/app/BentoCmake/modules/juce/modules/juce_core/unit_tests/juce_UnitTest.h: In instantiation of ‘void juce::UnitTest::expectResultAndPrint(ValueType, ValueType, bool, juce::String, juce::String) [with ValueType = unsigned int]’:
/home/pi/dev/app/BentoCmake/modules/juce/modules/juce_core/unit_tests/juce_UnitTest.h:154:30:   required from ‘void juce::UnitTest::expectEquals(ValueType, ValueType, juce::String) [with ValueType = unsigned int]’
/home/pi/dev/app/BentoCmake/modules/tracktion_graph/utilities/tracktion_Allocation.test.cpp:159:72:   required from here
/home/pi/dev/app/BentoCmake/modules/juce/modules/juce_core/unit_tests/juce_UnitTest.h:295:55: error: ambiguous overload for ‘operator<<’ (operand types are ‘juce::String’ and ‘unsigned int’)
  294 |             failureMessage << "Expected value" << (compDescription.isEmpty() ? "" : " ")
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  295 |                            << compDescription << ": " << valueToCompareTo
      |                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
/home/pi/dev/app/BentoCmake/modules/juce/modules/juce_core/unit_tests/juce_UnitTest.h:295:55: note: candidate: ‘operator<<(int, unsigned int)’ (built-in)

That looks like a failure at tracktion_Allocation.test.cpp:159. @dave96 may be able to advise on further steps.

Projucer writes JUCE_LDFLAGS += $(TARGET_ARCH) when generating a Makefile, where TARGET_ARCH can be one of -march=native, -m32, -m64, -march=armv6, -march=armv7, -march=armv8-a, or the empty string (the default).

1 Like

I made more tests… The StepSequencerDemo of tracktion_engine (https://github.com/toxvox/tracktion_engine_test/tree/master/examples/StepSequencerDemo) failed exactly in the same position.

With Ubuntu (x86) I can build StepSequencerDemo without any errors.

This are my build commands:
cmake . -B cmake-build
cmake --build cmake-build

Thanks for bringing that to my attention. I expected this flag to only have an effect on code generation, and I thought my skim of the ld manual showed that the flag wouldn’t do anything - but I’d forgotten that linkers often get called through compiler drivers.

I experimented a bit with clang’s -v flag and it does look like passing -m32 when linking with clang++ ends up calling ld with -m elf_i386 whereas -m64 invokes ld with -m elf_x86_64. Then I tried building a couple of TUs with -m64 and linking them with -m32, and got an error due to incompatible architectures. So, passing -m32 and -m64 during the linking stage has an effect after all. I stand corrected!

I don’t think that changes my original point though. If the build output is intended to run on a 32-bit machine, it’s still probably not a good idea to pass -m64 during the build.

1 Like

Thanks for digging into that topic. I did learn a few things here!

I added the -mcpu=native flag too for the error above and now am getting this error as well on my pi build.

Can I check what branch of tracktion_engine you’re building with? That sounds like an ambiguity we’ve cleaned up now on the develop branch (which got merged to master a fairly recently).

What is tracktion_Allocation.test.cpp: line 159 in your local copy?

I’m building with the develop branch.

tracktion_Allocation.test.cpp: Line 159

expectEquals<size_t> (intVec1.size(), 0);

If you change it to this does it compile?
expectEquals<size_t> (intVec1.size(), (size_t) 0);

unfortunately the same error…

I added the complete terminal output. Maybe it is helpful.

Build.txt (432.4 KB)

Ok, that’s annoying. It’s because there’s no unsigned int << operator on juce::String.

That probably means all those expectEquals<size_t> calls need to be changed to this style:
expect (intVec1.size() == 0); .

I don’t actually have a 32-bit Linux setup to test this with at the moment. You could do it locally to get it to build and I’ll try and sort this when I have a window.

any way we can get that merged into develop? Would be helpful as I currently would have to keep a custom fork of tracktion in order to build for the pi. FWIW, here is the file with the suggested changes added. Ive built and tested this and it compiles for the pi (arm-linux-gnueabihf) and x86-64 linux targets.

/*
    ,--.                     ,--.     ,--.  ,--.
  ,-'  '-.,--.--.,--,--.,---.|  |,-.,-'  '-.`--' ,---. ,--,--,      Copyright 2018
  '-.  .-'|  .--' ,-.  | .--'|     /'-.  .-',--.| .-. ||      \   Tracktion Software
    |  |  |  |  \ '-'  \ `--.|  \  \  |  |  |  |' '-' '|  ||  |       Corporation
    `---' `--'   `--`--'`---'`--'`--' `---' `--' `---' `--''--'    www.tracktion.com

    Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
*/

#include "../../3rd_party/rpmalloc/rpallocator.h"


namespace tracktion_graph
{

#if GRAPH_UNIT_TESTS_ALLOCATION

class AllocationTests    : public juce::UnitTest
{
public:
    AllocationTests()
        : juce::UnitTest ("Allocation", "tracktion_graph") {}

    //==============================================================================
    void runTest() override
    {
        runRPMallocTests();
    }
    
private:
    void runRPMallocTests()
    {
        beginTest ("rpmalloc single thread");
        {
            expectEquals (rpmalloc_initialize(), 0, "rpmalloc_initialize failed");
            
            {
                constexpr size_t numFloats = 256;
                constexpr size_t numBytes = numFloats * sizeof (float);
                auto data = static_cast<float*> (rpmalloc (numBytes));
                expect (data != nullptr);
                
                std::fill_n (data, numFloats, 0.7f);
                
                rpfree (data);
            }

            {
                constexpr size_t numFloats = 256;
                constexpr size_t numBytes = numFloats * sizeof (float);
                auto data = static_cast<float*> (rpmalloc (numBytes));
                expect (data != nullptr);
                
                std::fill_n (data, numFloats, 0.7f);
                
                rpfree (data);
            }

            rpmalloc_finalize();
        }

        beginTest ("rpmalloc multi thread");
        {
            expectEquals (rpmalloc_initialize(), 0, "rpmalloc_initialize failed");

            constexpr size_t numInts = 256;
            constexpr size_t numBytes = numInts * sizeof (float);
            int* data1 = nullptr, *data2 = nullptr;
            
            std::thread t1 ([&]
                            {
                                rpmalloc_thread_initialize();

                                data1 = static_cast<int*> (rpmalloc (numBytes));
                                expect (data1 != nullptr);
                                std::fill_n (data1, numInts, 42);
                                expectEquals (*data1, 42);
                                expectEquals (data1[numInts - 1], 42);
                                
                                rpmalloc_thread_finalize();
                            });
            
            std::thread t2 ([&]
                            {
                                t1.join();
                                rpmalloc_thread_initialize();

                                data2 = static_cast<int*> (rpmalloc (numBytes));
                                expect (data2 != nullptr);
                                std::fill_n (data2, numInts, 42);
                                expectEquals (*data2, 42);
                                expectEquals (data2[numInts - 1], 42);

                                expectEquals (*data1, 42);
                                expectEquals (data1[numInts - 1], 42);
                                rpfree (data1);
                                
                                rpmalloc_thread_finalize();
                            });
            
            t2.join();
            expectEquals (*data2, 42);
            expectEquals (data2[numInts - 1], 42);
            rpfree (data2);

            rpmalloc_finalize();
        }

        beginTest ("rpallocator");
        {
            std::vector<int, rpallocator<int>> intVec (1024, 0);
            std::fill (intVec.begin(), intVec.end(), 42);
            expectEquals (*intVec.begin(), 42);
            expectEquals (intVec[intVec.size() - 1], 42);

            intVec.push_back (43);
            expectEquals (*intVec.begin(), 42);
            expectEquals (intVec[intVec.size() - 1], 43);
        }

        beginTest ("rpallocator cross-thread");
        {
            std::vector<int, rpallocator<int>> intVec1 (1024, 1);

            std::thread t1 ([this, &intVec1]
                            {
                                expectEquals (*intVec1.begin(), 1);
                                expectEquals (intVec1[intVec1.size() - 1], 1);
                
                                std::vector<int, rpallocator<int>> intVec2 (1024, 2);
                                expectEquals (*intVec2.begin(), 2);
                                expectEquals (intVec2[intVec2.size() - 1], 2);

                                intVec1.push_back (43);
                                expectEquals (*intVec1.begin(), 1);
                                expectEquals (intVec1[intVec1.size() - 1], 43);
                            });

            std::thread t2 ([this, &intVec1, &t1]
                            {
                                t1.join();
                
                                intVec1.push_back (44);
                                expectEquals (*intVec1.begin(), 1);
                                expectEquals (intVec1[intVec1.size() - 1], 44);
                            });

            t2.join();
            intVec1.push_back (45);
            expectEquals (*intVec1.begin(), 1);
            expectEquals (intVec1[intVec1.size() - 3], 43);
            expectEquals (intVec1[intVec1.size() - 2], 44);
            expectEquals (intVec1[intVec1.size() - 1], 45);

            std::thread t3 ([this, &intVec1]
                            {
                                intVec1.clear();
                                expect (intVec1.size() == 0);
                                expect (intVec1.capacity() > 0);
                                intVec1.shrink_to_fit();
                                expect (intVec1.capacity() == 0);
                            });
            t3.join();
            expect (intVec1.size() == 0);
            expect (intVec1.capacity() == 0);
        }
        
        beginTest ("rpallocator multi-thread");
        {
            // Create 100 threads
            // Randomly push or pop ints in to a vector
            
            constexpr int numThreads = 100;
            std::vector<std::thread> pool;
            std::atomic<bool> shouldExit { false };

            for (int threadNum = 0; threadNum < numThreads; ++threadNum)
                pool.emplace_back ([&shouldExit]
                                   {
                                       const size_t maxSize = 100'000;
                                       juce::Random r;
                                       std::vector<int, rpallocator<int>> vec;
                    
                                       while (! shouldExit)
                                       {
                                           const bool remove = r.nextBool() || vec.size() >= maxSize;
                                           const auto num = (size_t) r.nextInt ({ 5, 6 + r.nextInt (5) });

                                           if (remove)
                                           {
                                               const auto numToRemove = std::min (num, vec.size());
                                               
                                               for (size_t i = 0; i < numToRemove; ++i)
                                                   vec.pop_back();
                                           }
                                           else
                                           {
                                               const auto numToAdd = (size_t) r.nextInt ({ 5, 6 + r.nextInt (5) });

                                               for (size_t i = 0; i < numToAdd; ++i)
                                                   vec.push_back (r.nextInt());
                                           }
                                       }

                                      #if LOG_RPALLOCATIONS
                                       std::cout << "num ints: " << vec.size() << "\n";
                                      #endif
                                   });
            
            using namespace std::literals;
            std::this_thread::sleep_for (1s);
            shouldExit = true;
            
            for (auto& thread : pool)
                thread.join();
            
            expect (true);
        }
    }
};

static AllocationTests allocationTests;

#endif

} // namespace tracktion_engine
1 Like

Do you have a fork that builds on the Pi? If so, can you open a PR so I can see exactly what needs changing. I don’t have a Pi build setup so it’s pretty hit and miss otherwise.

Works for me! Thanks!

Was this merged in already? I’m no longer getting the previous error with the latest develop branch of tracktion engine. I am getting a new error. The only way I’ve been able to fix it is to comment out line 161, intVec1.shrink_to_fit(); , then it compiles.
Not sure if there is a better solution. The error is:

from /home/lucian303/Phenotype/Builds/CMake/JuceLibraryCode/include_tracktion_graph.cpp:8:
/usr/include/c++/11/bits/stl_vector.h: In instantiation of ‘void std::vector<_Tp, _Alloc>::swap(std::vector<_Tp, _Alloc>&) [with _Tp = int; _Alloc = rpallocator<int>]’:
/usr/include/c++/11/bits/allocator.h:320:28:   required from ‘static bool std::__shrink_to_fit_aux<_Tp, true>::_S_do_it(_Tp&) [with _Tp = std::vector<int, rpallocator<int> >]’
/usr/include/c++/11/bits/vector.tcc:693:56:   required from ‘bool std::vector<_Tp, _Alloc>::_M_shrink_to_fit() [with _Tp = int; _Alloc = rpallocator<int>]’
/usr/include/c++/11/bits/stl_vector.h:990:9:   required from ‘void std::vector<_Tp, _Alloc>::shrink_to_fit() [with _Tp = int; _Alloc = rpallocator<int>]’
/home/lucian303/tracktion_engine/modules/tracktion_graph/utilities/tracktion_Allocation.test.cpp:161:54:   required from here
/usr/include/c++/11/bits/stl_vector.h:1483:9: error: no match for ‘operator==’ (operand types are ‘std::_Vector_base<int, rpallocator<int> >::_Tp_alloc_type’ {aka ‘std::allocator_traits<rpallocator<int> >::rebind_alloc<int>’} and ‘std::_Vector_base<int, rpallocator<int> >::_Tp_alloc_type’ {aka ‘std::allocator_traits<rpallocator<int> >::rebind_alloc<int>’})
 1483 |         __glibcxx_assert(_Alloc_traits::propagate_on_container_swap::value
      |         ^~~~~~~~~~~~~~~~
In file included from /usr/include/c++/11/bits/stl_algobase.h:67,
                 from /usr/include/c++/11/algorithm:61,
                 from /home/lucian303/tracktion_engine/modules/juce/modules/juce_core/system/juce_StandardHeader.h:46,
                 from /home/lucian303/tracktion_engine/modules/juce/modules/juce_core/juce_core.h:204,
                 from /home/lucian303/tracktion_engine/modules/juce/modules/juce_audio_basics/juce_audio_basics.h:54,
                 from /home/lucian303/tracktion_engine/modules/juce/modules/juce_audio_formats/juce_audio_formats.h:57,
                 from /home/lucian303/tracktion_engine/modules/tracktion_graph/tracktion_graph.cpp:21,
                 from /home/lucian303/Phenotype/Builds/CMake/JuceLibraryCode/include_tracktion_graph.cpp:8:

I have noticed this as well when trying to build on ubuntu 22.04 with both clang 14 and gcc 11.2.0. This occurs on x86_64, and is not an issue with 32 bit architectures or anything. It does seem to be shrink to fit causing the problem (or perhaps the rpmalloc allocator). @dave96 I would be grateful if you could patch a fix for this.

I’m preparing to make the feature/engine_2_0 branch the develop branch. Do you know if it happens there? The CI seems to build everything fine. I think it’s using GCC 7.2 for compatibility reasons but I don’t know why this would suddenly be an error?

I’ll see if I can add CI for the latest GCC soon as well.
Do you have a proposed patch to fix it?