Libraries Guide

Create, distribute, and use reusable Strada libraries.

Overview

Strada supports three 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

Archive Files (.a)

Static archive with bundled runtime. Use import_archive to import.

Best for: Self-contained libraries, distribution without runtime dependency

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.
Tip: Call other functions in the same package using ::func() shorthand:
func compute(int $x, int $y) int {
    return ::add($x, $y) * ::multiply($x, $y);
}

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/

For archive files (.a):

./strada --static-lib lib/MathLib.strada
mv MathLib.a lib/
Building standard libraries: Use ./configure and make libs to build the standard libraries (DBI, crypt, ssl, readline) with auto-detected dependencies:
./configure    # Detect MySQL, PostgreSQL, OpenSSL, etc.
make libs      # Build all libraries
Self-contained metadata: Function signatures are embedded in the compiled .so, .o, and .a files via __strada_export_info(). You don't need to keep the original .strada source file.
Archive includes runtime: Archives created with --static-lib include the Strada runtime, making them fully self-contained. The compiler automatically skips the runtime when linking against archives to avoid duplicate symbols.

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.so";  # 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.o";  # 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

Using Archive Files (import_archive)

Archive files are self-contained static libraries that include the Strada runtime. They're ideal for distributing libraries without requiring the runtime separately.

Basic Usage

use lib "lib";                # Add lib/ to search path
import_archive "MathLib.a";   # Link MathLib.a statically

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

Requirements

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

How It Works

  1. At compile time: Compiler extracts metadata from MathLib.a by calling __strada_export_info()
  2. Generates direct function calls (no wrappers needed)
  3. Links MathLib.a into the final executable
  4. Skips runtime linking since the archive includes it

Creating Archive Files

# Create archive with bundled runtime
./strada --static-lib lib/MathLib.strada
mv MathLib.a lib/
Runtime bundling: Archives created with --static-lib include the Strada runtime (strada_runtime.o). When you import an archive, the compiler automatically skips linking the runtime separately to avoid duplicate symbols.

Comparing Library Types

FeatureShared (.so)Object (.o)Archive (.a)
LinkingRuntime (dlopen)Compile timeCompile time
DistributionMultiple filesSingle executableSingle executable
Includes runtimeNoNoYes
Startup timeSlightly slower (lazy load)FasterFaster
Memory sharingShared between processesPer-processPer-process
UpdatesReplace .so without recompileRequires recompileRequires recompile
PluginsExcellentNot suitableNot suitable
Self-containedNo (needs runtime)No (needs runtime)Yes
Rule of thumb:
  • Use import_lib for optional features or plugins
  • Use import_object for core libraries you always need
  • Use import_archive for distributing self-contained libraries to users who don't have the Strada runtime

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.so";        # Searches all paths for JSON.so
import_object "Utils.o";     # Searches all paths for Utils.o
import_archive "DataLib.a"; # Searches all paths for DataLib.a

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 = core::dl_open("lib/JSON.so");
my int $fn = core::dl_sym($lib, "__strada_version");
my str $version = core::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 (runtime loading)
./strada --shared lib/JSON.strada && mv JSON.so lib/

# Or as object file (static linking)
./strada --object lib/JSON.strada && mv JSON.o lib/

# Or as archive file (static linking with bundled runtime)
./strada --static-lib lib/JSON.strada && mv JSON.a lib/

3. Use in Your Program

# app.strada
use lib "lib";
import_lib "JSON.so";  # or: import_object "JSON.o" or import_archive "JSON.a"

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

Standard Libraries

Strada includes several standard libraries that require external dependencies. Use ./configure and make libs to build them with auto-detected dependencies.

DBI (Database Interface)

The DBI library provides a unified interface for MySQL, SQLite, and PostgreSQL. The configure script detects which database drivers are available.

# Install database development libraries
sudo apt install libmysqlclient-dev   # MySQL
sudo apt install libsqlite3-dev       # SQLite
sudo apt install libpq-dev            # PostgreSQL

# Configure to detect databases
./configure

# Build DBI with detected drivers
make lib-dbi

# Output shows which drivers are enabled:
#   DBI drivers: MySQL=1 SQLite=1 PostgreSQL=0

Other Standard Libraries

LibraryMake TargetDependenciesPurpose
lib/DBI.somake lib-dbilibmysqlclient, libsqlite3, libpqDatabase interface
lib/crypt.somake lib-cryptlibcryptPassword hashing
lib/ssl.somake lib-ssllibssl, libcryptoSSL/TLS support
lib/readline/readline.somake lib-readlinelibreadlineLine editing for REPL
# Build all standard libraries at once
make libs

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/
cannot find LibName.a .a file not in search path Add use lib "path" or move .a 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
multiple definition Runtime linked twice with archive This shouldn't happen - compiler auto-skips runtime for archives