Compiling against a C++ submodule library


(BrainStone) #1

Hi. I’m trying to setup a C++ project that itself uses another (not gradle) C++ project.

Since the compilation doesn’t require anything special I tried including the project as a submodule and compiling that library as a library through gradle itself.

To my knowledge I have set up the build script correctly like this:

// Project-Type
apply plugin: "cpp"
// IDEs
apply plugin: "visual-studio"

model {
    visualStudio {
        solutions.all {
            solutionFile.location = "vs/${name}.sln"
            solutionFile.withContent { TextProvider content ->
                content.asBuilder().insert(0, "# GENERATED FILE: DO NOT EDIT\n")
                content.text = content.text.replaceAll("HideSolutionNode = FALSE", "HideSolutionNode = TRUE")
            }
        }
    }

    repositories {
        libs(PrebuiltLibraries) {
            easyloggingpp {
                headers.srcDir "lib/easyloggingpp/src"
            }
            eigen {
                headers.srcDir "lib/OpenNN/eigen/src"
            }
        }
    }

    platforms {
        x86 {
            architecture "x86"
        }
        x64 {
            architecture "x64"
        }
    }
    
    buildTypes {
        debug
        release
    }

    components {
        tinyxml2(NativeLibrarySpec) {
            sources.cpp {
                source {
                    srcDirs "lib/OpenNN"
                    include "tinyxml2/**/*.cpp"
                }
                exportedHeaders {
                    srcDirs "lib/OpenNN"
                    include "tinyxml2/**/*.h"
                }
            }
        }
        openNN(NativeLibrarySpec) {
            sources.cpp {
                source {
                    srcDirs "lib/OpenNN"
                    include "opennn/**/*.cpp"
                }
                exportedHeaders {
                    srcDirs "lib/OpenNN"
                    include "opennn/**/*.h"
                }
                
                lib library: "eigen", linkage: "api"
                lib library: "tinyxml2", linkage: "static"
            }
        }
        anni(NativeExecutableSpec) {
            if(System.properties['sun.arch.data.model'] == "64") {
                targetPlatform "x64"
            } else {
                targetPlatform "x86"
            }

            sources.cpp {
                source {
                    srcDirs "src"
                    include "**/*.cpp"
                }
                exportedHeaders {
                    srcDirs "src"
                    include "**/*.hpp"
                }
                
                lib library: "easyloggingpp", linkage: "api"
                lib library: "openNN", linkage: "static"
            }
        }
    }
    
    binaries {
        withType(NativeExecutableBinarySpec) {
            if (toolChain in Gcc) {
                cppCompiler.args "-Wall", "-Wextra", "-Wpedantic", "-fPIC"
            }
            if (toolChain in Clang) {
                cppCompiler.args "-Weverything", "-pedantic"
            }
            if (toolChain in VisualCpp) {
                cppCompiler.args "/W4", "/FS", "/EHsc"
            }
        }
        withType(SharedLibraryBinary) {
            buildable = false
        }
        withType(StaticLibraryBinarySpec) {
            if (toolChain in Gcc) {
                cppCompiler.args "-Werror"
            }
            if (toolChain in Clang) {
                cppCompiler.args "-Werror"
            }
            if (toolChain in VisualCpp) {
                cppCompiler.args "/W0", "/EHsc"
            }
        }
        all {
            if(buildType == buildTypes.debug) {
                cppCompiler.define "__DEBUG__"
    
                if (toolChain in Gcc) {
                    cppCompiler.args "-Og", "-g3"
                }
                if (toolChain in Clang) {
                    cppCompiler.args "-O0", "-g"
                }
                if (toolChain in VisualCpp) {
                    cppCompiler.args "/Od", "/Z7"
                }
            }
            if(buildType == buildTypes.release) {
                cppCompiler.define "__NDEBUG__"
    
                if (toolChain in Gcc) {
                    cppCompiler.args "-Ofast", "-g0"
                }
                if (toolChain in Clang) {
                    cppCompiler.args "-Ofast", "-g0"
                }
                if (toolChain in VisualCpp) {
                    cppCompiler.args "/O2"
                }
            }
        }
    }
}

Though when I run gradle build I get the following error:

Parallel execution is an incubating feature.

FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':linkAnniReleaseExecutable'.
> No static library binary available for library 'openNN' with [flavor: 'default', platform: 'x64', buildType: 'release']

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s

Can anyone help me with what I’m doing wrong?
(The repo can be found here and the version of the time of posting this question is: e518d77c4d)


What to do when nobody answers your topic?
(BrainStone) #2

I still haven’t managed to get it working. Can someone help me?


(Luke Daley) #3

@Daniel_L could you help here?


(Daniel Lacasse) #4

My bad for not responding earlier, we have been pretty busy with the future of native. The issue with the code sample is related to the targetPlatform of the declared components. The component anni declare a targetPlatform of x64 or x86, but all the other components use the default target platform which is the currently running platform. See this section of the user guide for more information.

The platform for anni can be either x86 or x64 where the platform for openNN will be the implicit default. You can solve the issue by specifying the following on all the other components (i.e. tinyxml2 and openNN):

targetPlatform 'x64'
targetPlatform 'x86'

I hope this helps. Don’t hesitate to ask more questions.


(BrainStone) #5

Thanks for the answer!

Is there a simple way I can globally apply the platforms? Like with an allprojects-block or similar?

Also I can’t seem to set the targetPlatform for PrebuiltLibraries

Neither

model {
    repositories {
        libs(PrebuiltLibraries) {
            targetPlatform "x64"

            easyloggingpp {
                headers.srcDir "easyloggingpp/src"
            }
        }
    }
    //...
}

nor

model {
    repositories {
        libs(PrebuiltLibraries) {
            easyloggingpp {
                targetPlatform "x64"

                headers.srcDir "easyloggingpp/src"
            }
        }
    }
    //...
}

works!

The first errors with

Could not find method targetPlatform() for arguments [x64] on prebuilt library 'easyloggingpp' of type org.gradle.nativeplatform.internal.prebuilt.DefaultPrebuiltLibrary.

And the second

Could not find method targetPlatform() for arguments [x64] on PrebuiltLibrary container of type org.gradle.nativeplatform.internal.prebuilt.DefaultPrebuiltLibraries.

I hope you can help me @Daniel_L!


(Daniel Lacasse) #6

Hey @BrainStone,

The expression targetPlatform 'x64' doesn’t work on a PrebuiltLibrary because the concept doesn’t exist for PrebuiltLibrary. In fact, binaries for PrebuiltLibrary are created for all combination of flavors, platforms and build types. You should be able to remove the expression, and everything should work.

As for defining targetPlatform for all project, you can do the following:

apply plugin: 'cpp'

allprojects {
    model {
        platforms {
            x64 {}
            x86 {}
        }
        components {
            all {
                targetPlatform 'x64'
                targetPlatform 'x86'
            }
        }
    }
}

model {
    components {
        main(NativeExecutableSpec)
        lib(NativeLibrarySpec)
    }
}

I hope this helps you get your build working.


(BrainStone) #7

I apologize for the extremely late response from my side. Life has kept me busy.

Anyways, I tried what you suggested, however it still seems to have issue with PrebuiltLibraries, as I’m getting this error for my header only libraries, which I have defined as PrebuiltLibraries:

Could not determine the dependencies of task ':linkAnniReleaseExecutable'.
> No shared library binary available for library 'easyloggingpp' with [flavor: 'default', platform: 'x64', buildType: 'release']

easyloggingpp is defined like this:

model {
    repositories {
        libs(PrebuiltLibraries) {
            easyloggingpp {
                headers.srcDir "easyloggingpp/src"
            }
        }
    }
}

And I include it in my executable like this:

lib project: ":lib", library: "easyloggingpp", linkage: "api"

Any idea how to fix that?


(Daniel Lacasse) #8

Hi @BrainStone,

Given the following build script that reproduces the failure:

allprojects {
    apply plugin: 'cpp'
}
project(':lib') {
    model {
        repositories {
            libs(PrebuiltLibraries) {
                easyloggingpp {
                    headers.srcDir project.rootProject.file("easyloggingpp/src")
                }
            }
        }
    }
}

 model {
     buildTypes {
         debug
         release
     }
     platforms {
         x64 {
             architecture 'x64'
         }
     }
    components {
        main(NativeExecutableSpec) {
            targetPlatform 'x64'
            binaries.all {
                lib project: ':lib', library: "easyloggingpp", linkage: "api"
            }
        }
    }
}

We can see that buildTypes and platforms is declared only on the main project where main executable is declared. To fix the issue, we need to declare the buildTypes and platforms in all projects by moving the declaration into the allprojects for example. The PreBuildLibraries automatically create variants for all dimensions declared, and the dependency management needs to match a variant with all the declared dimensions.

I hope this helps fix your issue,

Daniel


(BrainStone) #9

Weirdly enough, I use the allprojects block already: https://github.com/BrainStone/ANNI/blob/34591801e34f7bc3fa34364b421878146f4bf40c/build.gradle#L6-L17


(Daniel Lacasse) #10

You are indeed using the allprojects block to pass configuration specific to the cpp plugin. Unfortunately, the cpp plugin is applied outside the allprojects block which cause the project type to be applied only on the current project. In this case, it’s the root project. Moving the apply inside the allprojects block would solve the issue. You may also have the same issue with the visual-studio plugin.


(BrainStone) #11

I finally solved it by also adding the buildTypes into the allprojects block.
Feeling really stupid now xD