gradle / gradle-native

The home of Gradle's support for natively compiled languages
https://blog.gradle.org/introducing-the-new-cpp-plugins
Apache License 2.0
92 stars 8 forks source link

[Community Action Requested] Survey: Managing binaries/dependencies in native projects #985

Closed lacasseio closed 5 years ago

lacasseio commented 5 years ago

As part of our native effort, we want to better understand the use cases that native developers have with regards to managing binaries. Please comment on this issue with your stories, use cases, needs and wants regarding binaries that were built outside of Gradle ecosystem and that needs to be consumed inside your Gradle projects. When providing your feedback, it's important to make the distinction between what is required for your build compared to what is possible to do. We want to invest our initial effort where it will be the most beneficial for everyone. Thank you!

Guiding Questions

Some of our thoughts

It's important to make a clear distinction between the producer and the consumer of a binary. A consumer should only need to express what is needed, e.g. I need Qt.Core version 5 or I need Boost:Asio. Decisions about which variant you would need should be handled by Gradle's dependency management engine via attributes--such as VS2013 vs. VS2019 ABI, or single-threaded vs multi-threaded, or x86 vs x86-64. With this in mind, for consumers, we think dependencies should look at simple as they do now for Java projects. This works for external dependencies published by Gradle, but for other kinds of dependencies, there's nothing built in.

A producer should know a lot more about a binary, such as where to find it and which attributes apply to it. For instance, binaries can be found in known locations (e.g. /usr/lib and /usr/include), in a structured format (e.g. homebrew, MacPorts, chocolatey), discoverable (e.g. Windows SDK via registry or vswhere.exe) or chaotic (seemingly random include folders and/or library files scatter across a system). Some of these binaries may come with additional metadata that describe the attributes Gradle understands. We think producers should be responsible for providing these details to Gradle. We have some experiments in this area for using CMake to build a library and Gradle consuming it.

We understand some of the frustration when dealing with libraries in a native project. We believe this frustration comes from the lack of identity for dependencies that can creep into a project. We want to avoid a situation where include paths, libraries or arbitrary files without identities are needed to build a project. We would like existing Gradle tools like gradle dependencies to show you identities so it's easier to reason about the build.

zosrothko commented 5 years ago
k-mack commented 5 years ago

My responses are similar to @zosrothko's:

plugins {
    id "cpp-application"
}

def libBoostIncludePath = file("3rdparty/boost-1.60.0.2/include")

def linkLibs = [
    "linux:x86:debug:boost_program_options"     : file("3rdparty/boost-1.60.0.2/lib32/gcc4.8/libboost_program_options-debug.a"),
    "linux:x86:release:boost_program_options"   : file("3rdparty/boost-1.60.0.2/lib32/gcc4.8/libboost_program_options.a"),
    "linux:x86:debug:boost_system"              : file("3rdparty/boost-1.60.0.2/lib32/gcc4.8/libboost_system-debug.a"),
    "linux:x86:release:boost_system"            : file("3rdparty/boost-1.60.0.2/lib32/gcc4.8/libboost_system.a"),

    "linux:x86_64:debug:boost_program_options"  : file("3rdparty/boost-1.60.0.2/lib64/gcc4.8/libboost_program_options-debug.a"),
    "linux:x86_64:release:boost_program_options": file("3rdparty/boost-1.60.0.2/lib64/gcc4.8/libboost_program_options.a"),
    "linux:x86_64:debug:boost_system"           : file("3rdparty/boost-1.60.0.2/lib64/gcc4.8/libboost_system-debug.a"),
    "linux:x86_64:release:boost_system"         : file("3rdparty/boost-1.60.0.2/lib64/gcc4.8/libboost_system.a"),

    "windows:x86:debug:boost_program_options"   : file("3rdparty/boost-1.60.0.2/lib32/vs2015/boost_program_options-debug.lib"),
    "windows:x86:release:boost_program_options" : file("3rdparty/boost-1.60.0.2/lib32/vs2015/boost_program_options.lib"),
    "windows:x86:debug:boost_system"            : file("3rdparty/boost-1.60.0.2/lib32/vs2015/boost_system-debug.lib"),
    "windows:x86:release:boost_system"          : file("3rdparty/boost-1.60.0.2/lib32/vs2015/boost_system.lib"),

    "windows:x86_64:debug:boost_program_options"    : file("3rdparty/boost-1.60.0.2/lib64/vs2015/boost_program_options-debug.lib"),
    "windows:x86_64:release:boost_program_options"  : file("3rdparty/boost-1.60.0.2/lib64/vs2015/boost_program_options.lib"),
    "windows:x86_64:debug:boost_system"             : file("3rdparty/boost-1.60.0.2/lib64/vs2015/boost_system-debug.lib"),
    "windows:x86_64:release:boost_system"           : file("3rdparty/boost-1.60.0.2/lib64/vs2015/boost_system.lib"),
]

application {
    targetMachines = [
            machines.linux.x86,
            machines.linux.x86_64,
            machines.windows.x86,
            machines.windows.x86_64,
    ]

    binaries.whenElementFinalized { binary ->
        def arch = binary.targetMachine.architecture.name == MachineArchitecture.X86_64 ? "x86_64" : "x86"
        def os = binary.targetMachine.operatingSystemFamily.windows ? "windows" : "linux"
        def buildType = binary.optimized ? "release" : "debug"
        project.dependencies {
            add(binary.includePathConfiguration.name, files(libBoostIncludePath))
            add(binary.linkLibraries.name, files(linkLibs["${os}:${arch}:${buildType}:boost_program_options"]))
            add(binary.linkLibraries.name, files(linkLibs["${os}:${arch}:${buildType}:boost_system"]))
        }
    }
}
lacasseio commented 5 years ago

Thanks a lot @zosrothko and @k-mack for your responses. It seems tarball of the library is the usual way to consume dependencies for your use cases.

If Gradle was able to consume dependencies from various source (different package manager) would you envision fetching some dependencies from those package manager instead of tarball or the issue that "most libraries are compiled with specific flags" is too much of an issue for those package manager binaries?

Those samples are very helpful to see the pain you are going through. In my previous job, we didn't have much-prebuilt binaries, but I do think it's one of the big issue that need to be solved sooner.

lacasseio commented 5 years ago

Great feedback, it seems the most important use case identified here and by other channel is consuming prebuilt binaries from arbitrary packages. It should be possible to accomplish this now, a demonstration issue was open to create a sample to guide users. We will consider more out-of-the-box features once the sample have been written.