Using CMake flow to build libraries, Android apps

Thanks a lot, I’ll give it a try!

Your recipe above worked (so far), thanks!

A couple of minor caveats:

  1. I had to add_library() as STATIC rather than OBJECT for my JUCE dependencies library: CMake complained about cyclic dependencies. I guess this isn’t a problem though.

  2. I had to add the juce:: namespace everywhere (not a problem, just a note for anyone else following this)

  3. I wasn’t able to use the following:

     const juce::String getApplicationName() override
     {
        return juce::ProjectInfo::projectName;
     }
 
     const juce::String getApplicationVersion() override
     {
        return juce::ProjectInfo::versionString;
     }

in my juce::JUCEApplication; I assume juce::ProjectInfo is created along with JuceHeader.h. Not a show-stopper by any means; I can hard-code them for now and worst case, just define these some other way. Would be nice to re-use the JUCE way though.

1 Like

Following up on this thread @reuk @eyalamir as I am trying to contribute to Eyal’s CMake template, which has every project type except for the library type - as I mostly am making libraries it would be convenient to have a Github template set up with as much of the boiler plate as possible.

I tried the above recommendations, and unfortunately it seems that for me, the call

_juce_initialise_target(${TargetName})

is the single difference between the template building successfully or not. Without this line, there are many errors where juce_core’s .cpp files cannot find their own .h nor other symbols referenced from other headers.

Here’s what I have so far, if anyone can recommend a better way of doing it than this, thanks in advance!

project(LibraryTemplate VERSION 0.0.1)

set (TargetName ${PROJECT_NAME})

add_library(${TargetName} OBJECT)

_juce_initialise_target(${TargetName})

target_link_libraries(${TargetName}
        PRIVATE
        juce::juce_core
        PUBLIC
        juce::juce_recommended_config_flags
        juce::juce_recommended_warning_flags)

target_compile_definitions(${TargetName}
        INTERFACE
        $<TARGET_PROPERTY:${TargetName},COMPILE_DEFINITIONS>)

target_include_directories(${TargetName}
        INTERFACE
        $<TARGET_PROPERTY:${TargetName},INCLUDE_DIRECTORIES>)

set(SOURCES
    Source/LibraryAPI.cpp
    Source/LibraryImpl.cpp)

target_sources(${TargetName} PRIVATE ${SOURCES})

add_library(${TargetName}_shared SHARED)
add_library(${TargetName}_static STATIC)

target_link_libraries(${TargetName}_shared PUBLIC ${TargetName})
target_link_libraries(${TargetName}_static PUBLIC ${TargetName})

This might be a better reference, I was also trying to determine in what scenarios having both a static library and a consuming CLI app using juce_core would cause problems:

Maybe it was privately linking to juce_core in the static library that allowed things to (seemingly) work alright?

Here is also @eyalamir 's upstream equivalent, i noted he also needed to use the private _juce_initialise_target and so there is perhaps no better way to do this as of yet. (Caveat: I suspected in some projects I worked on that libraries could compile with JUCE without this call, but only if other non-library projects were part of the same larger CMake project and JUCE had the right visibility between parts of the larger project.)

I was also curious about the purpose of

target_compile_definitions(DLL PRIVATE JUCE_STANDALONE_APPLICATION=1)

as I did not need to add it to mine for things to work.

1 Like

When I remove it and try to build, I get the following warning:

#ifndef    JUCE_STANDALONE_APPLICATION
 JUCE_COMPILER_WARNING ("Please re-save your project with the latest Projucer version to avoid this warning")
 #define   JUCE_STANDALONE_APPLICATION 0
#endif

And I like my builds warning-free. :slight_smile:

2 Likes

So to return to a two year old convo:

On the Mac it turns out you can pre-link your static library and avoid this problem, details are here:

Hiding Symbols in Static Libraries with Xcode or CMake | Orange Juice Liberation Front).

In which he says:

A hackish way I found is to simply tell CMake to build a dynamic library, then modify the settings a bit. So instead of

add_library(mylibrary STATIC mylibrary.c mylibraryutils.c)

your CMake file says

add_library(mylibrary SHARED mylibrary.c mylibraryutils.c)

Of course, that just gets us a dylib. But it makes CMake call ld. Now that ld has a shot at our object files, we use

target_link_libraries(mylibrary -r)

which will sneakily insert the -r parameter that indicates we want to pre-link, not generate a dylib. So instead of a file of type MH_DYLIB we now get an MH_OBJECT file, a static lib just like we wanted. However, since CMake thinks we’re making a dylib, the generated file will still be named mylibrary.dylib even though it is static. So we need to override that suffix:

set_target_properties(foo PROPERTIES SUFFIX ".a")

I’m off to investigate for Windows. It’d be really useful to be able to build static libraries with the JUCE CMake system easily :slight_smile:

Hey, got something very similar running right now. Was just wondering if there is any way to use juce_generate_juce_header on targets that were made from add_library?

Probably not, but I figured it’d be worth asking

(A 17-months-later reply to your 11-months-later question… #SPEEDY)

If you look at the code for juce_generate_juce_header, it’s very simple and really relies on only two things:

  1. The target has a JUCE_GENERATED_SOURCES_DIRECTORY property, which is used to set ${juce_header}, the path to the JuceHeader.h that will be generated.
  2. The target has a JUCE_DEFS_FILE property, which will be set as ${defs_file}.

Both of those paths are then passed to the juceaide program in a custom command, the command line for which is:

juce::juceaide header ${defs_file} ${juce_header}

JuceAide is what generates the JuceHeader.h file.

The question becomes why you’d want to call juce_generate_juce_header at all, never mind on a non-JUCE target. As the JUCE CMake API docs say:

In plain CMake projects which don’t require Projucer compatibility, the use of JuceHeader.h is optional. Instead, module headers can be included directly in source files that require them.

Other than for ProJucer compatibility, there’s no need for a JuceHeader.h anymore, and you’re better off using more granular module #includes where necessary.

My previous post was in reply to you, I thought. But I notice it’s not showing up that way in the thread, so just adding this direct reply to trigger a notification.