gradle-native icon indicating copy to clipboard operation
gradle-native copied to clipboard

Introduce guide for building native library

Open lacasseio opened this issue 2 years ago • 1 comments

Similar to https://github.com/nokeedev/gradle-native/issues/626, the guide teaches the user how to build a library with Gradle and Nokee. There are 3 important sections: 1) simplest build creates a shared library, 2) targetLinkages configuration to create a static only library or a dual linkage library and 3) how to use the library (via dependencies).

The goal of the guide is not to understand dependency management but to demonstrate the library can be used as intended.

0. Setup

$ mkdir -p demo/demo_lib
$ cd demo
$ echo "include 'demo_lib'" > demo/settings.gradle

1. Simplest build

Sources in src/main/c, src/main/headers (private headers), and src/main/public (public headers, exported to consumers)

.demo_lib/build.gradle

plugins {
    id 'dev.nokee.c-library'
}

The library is built into build/libs/main/libdemo_lib.dylib (on macOS), build/libs/main/libdemo_lib.so (on other *nix), and build/libs/main/demo_lib.dll (on Windows). Additionally, Windows will produce a .lib file side-by-side with the .dll. This file represents the import library and contains the redirection to the correct ordinal of the shared library. This file may be absent if the shared library does not export any symbols (via __declspec(dllexport) or .def file). In this guide, we use declspec on Windows. Some use cases may include using the shared library dynamically thus not requiring explicitly linking against the import library, which is not covered in this guide.

The object files are located in build/objs and options.txt are in build/tmp/<task-name>.

2. Target linkages

.demo_lib/build.gradle

plugins {
    id 'dev.nokee.c-library'
}

library {
    targetLinkages = [linkages.static] // <1>
}

<1> targetLinkages is a component dimension (ListProperty). linkages is the TargetLinkage factory which create a static or shared linkage. Here, we set the targetLinkages to a single element list with only the static linkage.

It may be important to mention that linkages are "how the object files are linked together" either as a shared library (.dylib, .so, or .dll) or static library (.a, or .lib)

The library is built into build/libs/main/libdemo_lib.a (on *nix), and build/libs/main/demo_lib.lib (on Windows). Not the .lib on Windows is a static library, not an import library meaning the is no additional runtime requirement from the library aside from it's own shared library dependencies if any.

If we which to create a dual linkage library, we just have to set the targetLinkages to a collection of both linkages:

.demo_lib/build.gradle

library {
    targetLinkages = [linkages.static, linkages.shared]
}

3. Using the library

$ mkdir demo_app
$ echo "include 'demo_app' >> settings.gradle

.demo_app/build.gradle

plugins {
    id 'dev.nokee.c-application'
}

application {
    dependencies {
        implementation project(':demo_lib') // <1>
    }
}

<1> If only one linkage is declared, the build will pick that one. However, if multiple linkages are declared, the build will select the shared linkage by default.

To select the static linkage when both linkages are selected, we need to configure the dependency's attribute:

application {
    dependencies {
        implementation(project(':demo_lib')) {
            attributes {
                attribute(BinaryLinkage.BINARY_LINKAGE_ATTRIBUTE, objects.named(BinaryLinkage, "static"))
            }
        }
    }
}

Open Question about this guide

Convenience for linkage configuration on dependency

Binary linkage on dependency may be a bit more complicated for first-time users. Maybe we should introduce a similar construct as DependencyHandler#platform. Maybe something like implementation useStaticLibrary(project(':demo_lib')) Configuration of dependency attributes is also error-prone and quite verbose.

Sub-project names

It's generally considered best practice to use sub-projects instead of using directly the root project. In this case, we want two projects: 1) the library (subject of the guide) and 2) an application (demonstrate how to use the library). Gradle's sample/guides often use lib and app for the project name but that would create file names like liblib.dylib or worst lib.lib which may be more confusing to the user. We could use project names like greeter (library) and hello (application) instead which better represent each project.

lacasseio avatar May 16 '22 18:05 lacasseio

The challenge with this guide is to give a concise explanation of the shared library vs static library without going too much into the details. Nokee focus on building libraries not on how native libraries works (which differ a bit between OS). Unfortunately, it's a bit too common for beginners to not have a clear understanding of what are the various native components and how they work together. Understanding those basic concepts is outside the scope of this guide but we can at least give a bit of information so users with little to no experience can understand while more advanced users can focus on learning the concept Nokee users for building native components (in this case, libraries).

lacasseio avatar May 16 '22 18:05 lacasseio