Libraries Guide

Create, distribute, and use reusable Strada libraries.

Overview

Strada supports two types of libraries:

Shared Libraries (.so)

Dynamically loaded at runtime. Use import_lib to import.

Best for: Plugins, optional features, reducing executable size

Object Files (.o)

Statically linked at compile time. Use import_object to import.

Best for: Core libraries, single-file distribution, faster startup

Creating a Library

Step 1: Write Your Library

Create a Strada file with a package declaration and optional version:

# lib/MathLib.strada
package MathLib;
version "1.0.0";

func add(int $a, int $b) int {
    return $a + $b;
}

func multiply(int $a, int $b) int {
    return $a * $b;
}

func factorial(int $n) int {
    if ($n <= 1) {
        return 1;
    }
    return $n * factorial($n - 1);
}
Package naming: Functions are automatically prefixed with the package name. func add() becomes MathLib_add() internally.

Step 2: Compile the Library

For shared libraries (.so):

./strada --shared lib/MathLib.strada
mv MathLib.so lib/

For object files (.o):

./strada --object lib/MathLib.strada
mv MathLib.o lib/
Self-contained metadata: Function signatures are embedded in the compiled .so and .o files via __strada_export_info(). You don't need to keep the original .strada source file.

Using Shared Libraries (import_lib)

Shared libraries are loaded at runtime using dlopen(). They're loaded lazily on first function call.

Basic Usage

use lib "lib";          # Add lib/ to search path
import_lib "MathLib";    # Import MathLib.so

func main() int {
    # Call with namespace syntax (recommended)
    my int $sum = MathLib::add(10, 32);
    my int $product = MathLib::multiply(6, 7);
    my int $fact = MathLib::factorial(5);

    say("Sum: " . $sum);           # 42
    say("Product: " . $product);   # 42
    say("Factorial: " . $fact);   # 120

    return 0;
}

Requirements

FilePurposeRequired
LibName.soCompiled code + embedded metadataYes
LibName.stradaSource (only needed for recompiling)No

How It Works

  1. At compile time: Compiler loads MathLib.so and calls __strada_export_info() to extract function signatures
  2. Compiler generates wrapper functions that call into the shared library
  3. At runtime: First call to any library function loads MathLib.so via dlopen()
  4. Subsequent calls use cached function pointers

Using Object Files (import_object)

Object files are linked directly into your executable at compile time. The result is a single, self-contained binary.

Basic Usage

use lib "lib";             # Add lib/ to search path
import_object "MathLib";   # Link MathLib.o statically

func main() int {
    # Same syntax as import_lib
    my int $sum = MathLib::add(10, 32);
    say($sum);  # 42
    return 0;
}

Requirements

FilePurposeRequired
LibName.oCompiled code + embedded metadataYes
LibName.stradaSource (only needed for recompiling)No

How It Works

  1. At compile time: Compiler extracts metadata from MathLib.o by calling __strada_export_info()
  2. Generates direct function calls (no wrappers needed)
  3. Links MathLib.o into the final executable with gcc

Shared vs Object: When to Use Which

FeatureShared (.so)Object (.o)
LinkingRuntime (dlopen)Compile time (gcc)
DistributionMultiple filesSingle executable
Startup timeSlightly slower (lazy load)Faster
Memory sharingShared between processesPer-process
UpdatesReplace .so without recompileRequires recompile
PluginsExcellentNot suitable
Rule of thumb: Use import_object for core libraries you always need. Use import_lib for optional features or plugins.

Library Search Paths

The compiler searches for libraries in this order:

  1. Current directory
  2. Paths added with use lib "path" (in order)
  3. Paths added with -L path flag (high priority)
  4. Paths added with -LL path flag (low priority)
# Multiple search paths
use lib "lib";
use lib "vendor/lib";
use lib "/usr/local/strada/lib";

import_lib "JSON";      # Searches all paths for JSON.so and JSON.strada
import_object "Utils";  # Searches all paths for Utils.o and Utils.strada

Library Versioning

Add a version to your library with the version statement:

package JSON;
version "2.1.0";

func encode(scalar $data) str {
    # ...
}

Retrieve the version at runtime:

my int $lib = sys::dl_open("lib/JSON.so");
my int $fn = sys::dl_sym($lib, "__strada_version");
my str $version = sys::dl_call_version($fn);
say("JSON version: " . $version);  # "2.1.0"

Inspecting Libraries

Use the soinfo tool to inspect compiled libraries:

# View library information
./tools/soinfo lib/JSON.so

# Show usage examples
./tools/soinfo --examples lib/JSON.so

Example output:

Library: lib/JSON.so
Package: JSON
Version: 2.1.0

Exported functions:
  JSON_encode(scalar $data) -> str
  JSON_decode(str $json) -> scalar
  JSON_pretty(scalar $data) -> str

Call examples:
  my str $result = JSON::encode($data);
  my scalar $result = JSON::decode($json);
  my str $result = JSON::pretty($data);

Complete Example: JSON Library

1. Create the Library

# lib/JSON.strada
package JSON;
version "1.0.0";

func encode(scalar $data) str {
    return json_encode($data);
}

func decode(str $json) scalar {
    return json_decode($json);
}

func pretty(scalar $data) str {
    # Pretty print with indentation
    return json_encode_pretty($data, 2);
}

2. Compile

# As shared library
./strada --shared lib/JSON.strada && mv JSON.so lib/

# Or as object file
./strada --object lib/JSON.strada && mv JSON.o lib/

3. Use in Your Program

# app.strada
use lib "lib";
import_lib "JSON";  # or: import_object "JSON"

func main() int {
    # Create some data
    my hash %user = {
        "name" => "Alice",
        "age" => 30,
        "roles" => ["admin", "user"]
    };

    # Encode to JSON
    my str $json = JSON::encode(\%user);
    say($json);
    # {"name":"Alice","age":30,"roles":["admin","user"]}

    # Pretty print
    my str $pretty = JSON::pretty(\%user);
    say($pretty);

    # Decode back
    my scalar $decoded = JSON::decode($json);
    say($decoded->{"name"});  # Alice

    return 0;
}

4. Compile and Run

./strada app.strada
./app

Libraries with C Code

Libraries can include embedded C code using __C__ blocks:

# lib/Compress.strada
package Compress;
version "1.0.0";

__C__ {
    #include <zlib.h>
}

func gzip(str $data) str {
    __C__ {
        char *input = strada_to_str(data);
        size_t input_len = strlen(input);
        size_t output_len = compressBound(input_len);
        char *output = malloc(output_len);

        compress((Bytef*)output, &output_len,
                 (Bytef*)input, input_len);

        free(input);
        StradaValue *result = strada_new_str_len(output, output_len);
        free(output);
        return result;
    }
}

Compile with additional libraries:

./strada --shared lib/Compress.strada -- -lz

Troubleshooting

Common Errors

ErrorCauseSolution
cannot find LibName.so .so file not in search path Add use lib "path" or move .so to lib/
cannot find LibName.o .o file not in search path Add use lib "path" or move .o to lib/
undefined symbol Function not exported Check package name matches function prefix
missing __strada_export_info Old library format Recompile library with current compiler