Skip to content

Linux Platform

Complete guide to building C++ libraries for Linux with CCGO.

Overview

CCGO provides comprehensive Linux support with:

  • Multiple compilers: GCC and Clang
  • Multiple architectures: x86_64, ARM64 (aarch64), ARMv7
  • Output formats: Static libraries (.a), Shared libraries (.so)
  • Build methods: Local or Docker (cross-platform)
  • IDE support: CodeLite project generation
  • Distribution compatibility: Ubuntu, Debian, CentOS, Fedora, Alpine
  • C library variants: glibc and musl support

Prerequisites

Option 1: Local Build (Linux Required)

For Ubuntu/Debian:

# Install GCC toolchain
sudo apt update
sudo apt install -y build-essential cmake pkg-config

# Install Clang (optional)
sudo apt install -y clang

# Verify installation
gcc --version
g++ --version
cmake --version

For CentOS/Fedora/RHEL:

# Install GCC toolchain
sudo yum groupinstall -y "Development Tools"
sudo yum install -y cmake

# Or on Fedora
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y cmake

# Install Clang (optional)
sudo yum install -y clang
# Or on Fedora
sudo dnf install -y clang

# Verify
gcc --version
g++ --version
cmake --version

For Alpine Linux:

# Install GCC toolchain (musl libc)
apk add --no-cache build-base cmake

# Install Clang (optional)
apk add --no-cache clang

# Verify
gcc --version
g++ --version
cmake --version

Option 2: Docker Build (Any OS)

Build Linux libraries on macOS or Windows using Docker.

Required: - Docker Desktop installed and running - 3GB+ disk space for Docker image

Advantages: - Build on any operating system - Consistent build environment - No Linux dependencies required - Support for multiple Linux distributions

Limitations: - Cannot run/test applications (unless using Docker shell) - Larger initial download (~800MB image)

See Docker Builds section for details.

Quick Start

Basic Build

# Build for x86_64 with default compiler
ccgo build linux

# Build with Docker (cross-compile from any OS)
ccgo build linux --docker

# Specify compiler
ccgo build linux --compiler gcc       # GCC (default)
ccgo build linux --compiler clang     # Clang
ccgo build linux --compiler auto      # Both compilers

# Specify architecture
ccgo build linux --arch x86_64        # 64-bit Intel/AMD (default)
ccgo build linux --arch arm64         # 64-bit ARM (aarch64)
ccgo build linux --arch armv7         # 32-bit ARM

# Build types
ccgo build linux --build-type debug    # Debug build
ccgo build linux --build-type release  # Release build (default)

# Link types
ccgo build linux --build-as static    # Static library only
ccgo build linux --build-as shared    # Shared library only
ccgo build linux --build-as both      # Both types (default)

Generate CodeLite Project

# Generate CodeLite workspace
ccgo build linux --ide-project

# Open in CodeLite
codelite cmake_build/linux/MyLib.workspace

Output Structure

Default Output (target/linux/)

target/linux/
├── MyLib_Linux_SDK-1.0.0.zip           # Main package
│   ├── lib/
│   │   ├── static/
│   │   │   ├── gcc/
│   │   │   │   └── libmylib.a          # GCC static library
│   │   │   └── clang/
│   │   │       └── libmylib.a          # Clang static library
│   │   └── shared/
│   │       ├── gcc/
│   │       │   ├── libmylib.so         # GCC shared library
│   │       │   └── libmylib.so.1.0.0   # Versioned library
│   │       └── clang/
│   │           ├── libmylib.so
│   │           └── libmylib.so.1.0.0
│   ├── include/
│   │   └── mylib/                      # Header files
│   │       ├── mylib.h
│   │       └── version.h
│   └── build_info.json                 # Build metadata
└── MyLib_Linux_SDK-1.0.0-SYMBOLS.zip   # Debug symbols
    └── symbols/
        ├── gcc/
        │   └── libmylib.so.debug       # GCC debug symbols
        └── clang/
            └── libmylib.so.debug       # Clang debug symbols

Library Types

Static library (.a): - Archive of object files - Linked at compile time - Larger executable - No runtime dependencies - All symbols included

Shared library (.so): - Dynamically linked at runtime - Smaller executable - Shared between processes - Versioned (libmylib.so.1.0.0) - Symlinks for compatibility: - libmylib.solibmylib.so.1libmylib.so.1.0.0

Build Metadata

build_info.json contains:

{
  "project": {
    "name": "MyLib",
    "version": "1.0.0",
    "description": "My Linux library"
  },
  "build": {
    "platform": "linux",
    "architectures": ["x86_64"],
    "compilers": ["gcc", "clang"],
    "build_type": "release",
    "link_types": ["static", "shared"],
    "timestamp": "2024-01-15T10:30:00Z",
    "ccgo_version": "0.1.0",
    "gcc_version": "11.4.0",
    "clang_version": "14.0.0",
    "libc": "glibc-2.35"
  },
  "outputs": {
    "libraries": {
      "gcc": {
        "static": "lib/static/gcc/libmylib.a",
        "shared": "lib/shared/gcc/libmylib.so"
      },
      "clang": {
        "static": "lib/static/clang/libmylib.a",
        "shared": "lib/shared/clang/libmylib.so"
      }
    },
    "headers": "include/mylib/",
    "symbols": {
      "gcc": "symbols/gcc/libmylib.so.debug",
      "clang": "symbols/clang/libmylib.so.debug"
    }
  }
}

GCC vs Clang

GCC (GNU Compiler Collection)

Pros: - Default on most Linux distributions - Excellent optimization - Wide architecture support - Better C++20/C++23 support (recent versions) - Larger community

Cons: - Slower compilation than Clang - Less helpful error messages - Larger binaries sometimes

When to use: - Standard Linux development - Maximum compatibility - Latest C++ standards - Best optimization

Clang (LLVM)

Pros: - Faster compilation - Better error messages and warnings - Excellent static analysis - Better for development - Modular architecture

Cons: - May produce slightly slower code - Less common as default - Some architectures less optimized

When to use: - Development and debugging - Need better diagnostics - Using LLVM ecosystem - Static analysis required

Using Libraries in C++

Linking Static Library

CMakeLists.txt:

# Find library
find_library(MYLIB_LIBRARY
    NAMES mylib libmylib.a
    PATHS "/path/to/lib/static/gcc"
)

# Link to target
target_link_libraries(myapp PRIVATE ${MYLIB_LIBRARY})
target_include_directories(myapp PRIVATE "/path/to/include")

Manual compilation:

# With GCC
g++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/static/gcc -lmylib

# With Clang
clang++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/static/clang -lmylib

Linking Shared Library

CMakeLists.txt:

# Find shared library
find_library(MYLIB_LIBRARY
    NAMES mylib
    PATHS "/path/to/lib/shared/gcc"
)

target_link_libraries(myapp PRIVATE ${MYLIB_LIBRARY})
target_include_directories(myapp PRIVATE "/path/to/include")

# Set RPATH for finding library at runtime
set_target_properties(myapp PROPERTIES
    BUILD_RPATH "/path/to/lib/shared/gcc"
    INSTALL_RPATH "$ORIGIN:$ORIGIN/../lib"
)

Manual compilation:

# With GCC
g++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/shared/gcc -lmylib \
    -Wl,-rpath,/path/to/lib/shared/gcc

# Run with LD_LIBRARY_PATH
LD_LIBRARY_PATH=/path/to/lib/shared/gcc ./myapp

Using in code:

#include <mylib/mylib.h>

int main() {
    mylib::MyClass obj;
    obj.do_work();
    return 0;
}

Docker Builds

Build Linux libraries on any OS using Docker:

Prerequisites

# Install Docker Desktop
# Download from: https://www.docker.com/products/docker-desktop/

# Verify Docker is running
docker ps

Build with Docker

# First build downloads prebuilt image (~800MB)
ccgo build linux --docker

# Subsequent builds are fast
ccgo build linux --docker --arch x86_64

# All standard options work
ccgo build linux --docker --compiler gcc --build-as static

How It Works

  1. CCGO uses prebuilt ccgo-builder-linux image from Docker Hub
  2. Project directory mounted into container
  3. Build runs with Ubuntu 22.04 + GCC/Clang
  4. Output written to host filesystem

Limitations

  • Cannot run: No X11 display in Docker
  • Cannot test: GUI applications won't work
  • Use docker exec -it <container> bash to run CLI apps

Distribution Compatibility

glibc vs musl

glibc (GNU C Library): - Standard on most distributions - Better performance - Wider compatibility - Larger binary size

musl: - Used on Alpine Linux - Smaller and simpler - Static linking friendly - Strictly POSIX compliant

ABI Compatibility

Libraries built on older distributions generally work on newer ones:

# Build on Ubuntu 18.04 (glibc 2.27)
# Works on Ubuntu 20.04, 22.04, 24.04

# Build on Ubuntu 22.04 (glibc 2.35)
# May NOT work on Ubuntu 18.04, 20.04

Best practice: Build on the oldest distribution you need to support.

Versioning Shared Libraries

CCGO automatically creates versioned shared libraries:

# Created files
libmylib.so.1.0.0         # Real library with full version
libmylib.so.1             # SONAME symlink
libmylib.so               # Development symlink

# Check SONAME
objdump -p libmylib.so.1.0.0 | grep SONAME
# Output: SONAME      libmylib.so.1

Platform Configuration

CCGO.toml Settings

[package]
name = "mylib"
version = "1.0.0"

[library]
type = "both"                  # static, shared, or both

[build]
cpp_standard = "17"            # C++ standard

[linux]
compiler = "gcc"               # gcc, clang, or auto
libc = "glibc"                 # glibc or musl
position_independent_code = true  # PIC for shared libraries
strip_symbols = false          # Strip debug symbols

CMake Variables

When building for Linux:

${PLATFORM}                    # "linux"
${ARCHITECTURE}                # "x86_64", "arm64", or "armv7"
${BUILD_TYPE}                  # "Debug" or "Release"
${LINK_TYPE}                   # "static", "shared", or "both"
${COMPILER}                    # "gcc" or "clang"
${LINUX_LIBC}                  # "glibc" or "musl"

Conditional Compilation

// Platform detection
#ifdef __linux__
    // Linux-specific code
    #include <unistd.h>

    #ifdef __x86_64__
        // x86_64-specific code
    #elif defined(__aarch64__)
        // ARM64-specific code
    #elif defined(__arm__)
        // ARMv7-specific code
    #endif
#endif

// Compiler detection
#ifdef __GNUC__
    // GCC or Clang
    #define MYLIB_API __attribute__((visibility("default")))

    #ifdef __clang__
        // Clang-specific code
    #else
        // GCC-specific code
    #endif
#endif

// glibc detection
#ifdef __GLIBC__
    // glibc-specific code
    #include <gnu/libc-version.h>
#endif

// Usage
class MYLIB_API MyClass {
public:
    void do_work();
};

Symbol Visibility

Control exported symbols in shared libraries:

// mylib_export.h
#ifdef __linux__
    #ifdef MYLIB_EXPORTS
        #define MYLIB_API __attribute__((visibility("default")))
    #else
        #define MYLIB_API
    #endif
    #define MYLIB_LOCAL __attribute__((visibility("hidden")))
#else
    #define MYLIB_API
    #define MYLIB_LOCAL
#endif

// Public API
class MYLIB_API PublicClass {
public:
    void public_method();
};

// Internal implementation (not exported)
class MYLIB_LOCAL InternalClass {
public:
    void internal_method();
};

Best Practices

1. Control Symbol Visibility

Hide internal symbols to reduce library size and improve loading time:

# CMakeLists.txt
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
// Explicitly export public API
class __attribute__((visibility("default"))) MyPublicClass { ... };

2. Use RPATH for Distribution

Set RPATH so applications can find shared libraries:

# Install libraries to lib/ directory relative to binary
set_target_properties(myapp PROPERTIES
    INSTALL_RPATH "$ORIGIN:$ORIGIN/../lib"
)

Directory structure:

myapp/
├── bin/
│   └── myapp              # Executable
└── lib/
    └── libmylib.so        # Libraries

3. Version Shared Libraries

Follow semantic versioning for shared libraries:

[package]
version = "1.2.3"          # Creates libmylib.so.1.2.3

4. Static Linking for Simplicity

For simpler distribution without dependencies:

# Build static library only
ccgo build linux --build-as static

# All code is embedded in executable
g++ -o myapp main.cpp -I/path/to/include -L/path/to/lib/static -lmylib

5. Test on Target Distributions

Always test on actual target distributions: - Ubuntu 20.04, 22.04, 24.04 - Debian 11, 12 - CentOS 7, 8, 9 - Fedora (latest) - Alpine (for musl)

6. Use Position Independent Code

Always enable PIC for shared libraries:

[linux]
position_independent_code = true

7. Strip Release Binaries

Reduce library size for release builds:

# Strip debug symbols
strip libmylib.so

# Or configure in CCGO.toml
[linux]
strip_symbols = true

Troubleshooting

Compiler Not Found

Error: g++ not found

Solution:

# Ubuntu/Debian
sudo apt install -y build-essential

# CentOS/Fedora
sudo yum groupinstall -y "Development Tools"

# Verify
which g++
g++ --version

Library Not Found at Runtime

error while loading shared libraries: libmylib.so: cannot open shared object file

Solutions:

  1. Add to LD_LIBRARY_PATH:

    export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH
    ./myapp
    

  2. Install to system directory:

    sudo cp libmylib.so /usr/local/lib/
    sudo ldconfig
    

  3. Use RPATH (recommended):

    set_target_properties(myapp PROPERTIES
        INSTALL_RPATH "$ORIGIN/../lib"
    )
    

  4. Check library path:

    ldd myapp
    # Shows: libmylib.so => not found
    
    # After fixing:
    ldd myapp
    # Shows: libmylib.so => /path/to/lib/libmylib.so
    

Symbol Not Found

undefined reference to 'mylib::MyClass::do_work()'

Solutions:

  1. Check symbol exists:

    nm -C libmylib.so | grep do_work
    # Should show: 00001234 T mylib::MyClass::do_work()
    

  2. Verify library is linked:

    ldd myapp | grep mylib
    # Should show libmylib.so
    

  3. Check symbol visibility:

    // Ensure symbols are exported
    class __attribute__((visibility("default"))) MyClass { ... };
    

Version Mismatch

version `GLIBC_2.35' not found

Solution:

Build on an older distribution or use static linking:

# Check required glibc version
objdump -T libmylib.so | grep GLIBC

# Check system glibc version
ldd --version

# Build on older system or use Docker with older base image

CMake Configuration Fails

Could not find a package configuration file provided by "MyLib"

Solution:

Ensure CMake can find the library:

# Set CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH "/path/to/MyLib/lib/cmake")
find_package(MyLib REQUIRED)

# Or set as environment variable
export CMAKE_PREFIX_PATH=/path/to/MyLib/lib/cmake

Performance Tips

[build]
cxxflags = ["-flto"]       # Enable LTO
ldflags = ["-flto"]

2. Enable Optimizations

[build]
cxxflags = [
    "-O3",                 # Maximum optimization
    "-march=native",       # Use CPU-specific instructions
    "-mtune=native"
]

3. Profile-Guided Optimization

# 1. Build with profiling
CXXFLAGS="-fprofile-generate" ccgo build linux

# 2. Run with typical workload
./benchmark

# 3. Rebuild with profile data
CXXFLAGS="-fprofile-use" ccgo build linux

4. Static Linking for Performance

Static linking can be faster due to better optimization:

ccgo build linux --build-as static

5. Disable Exceptions (If Not Needed)

[build]
cxxflags = ["-fno-exceptions"]

Packaging and Distribution

System Package Integration

Debian/Ubuntu (.deb):

# Install checkinstall
sudo apt install checkinstall

# Create .deb package
cd target/linux
sudo checkinstall --pkgname=mylib --pkgversion=1.0.0 \
    --provides=mylib make install

RPM-based (.rpm):

# Create RPM package
rpmbuild -ba mylib.spec

AppImage (Portable):

# Bundle application with dependencies
appimagetool myapp.AppDir myapp.AppImage

Snap Package

# snapcraft.yaml
name: mylib
version: '1.0.0'
summary: My Linux library
description: Complete C++ library for Linux

parts:
  mylib:
    plugin: cmake
    source: .
# Build snap
snapcraft

Flatpak

{
  "app-id": "com.example.mylib",
  "runtime": "org.freedesktop.Platform",
  "sdk": "org.freedesktop.Sdk",
  "command": "myapp"
}
# Build flatpak
flatpak-builder build-dir com.example.mylib.json

Migration Guides

From Makefile

Before:

CC = gcc
CFLAGS = -O2 -Wall
TARGET = libmylib.so

$(TARGET): mylib.o
    $(CC) -shared -o $(TARGET) mylib.o

After:

  1. Create CCGO project:

    ccgo new mylib
    

  2. Copy source files to src/

  3. Configure CCGO.toml:

    [linux]
    compiler = "gcc"
    

  4. Build:

    ccgo build linux
    

From CMake

CMakeLists.txt:

project(mylib)
add_library(mylib SHARED src/mylib.cpp)
target_include_directories(mylib PUBLIC include)

CCGO.toml:

[package]
name = "mylib"
version = "1.0.0"

[library]
type = "shared"

Then: ccgo build linux

From Autotools

Before:

./configure
make
make install

After:

  1. Extract source files from src/ directory
  2. Create CCGO project with ccgo new
  3. Copy sources to new project structure
  4. Configure dependencies in CCGO.toml
  5. Build with ccgo build linux

Advanced Topics

Cross-Compilation

Build for different architectures:

# Build for ARM64 on x86_64
ccgo build linux --arch arm64 --docker

# Build for ARMv7
ccgo build linux --arch armv7 --docker

Static musl Builds

For truly portable static binaries:

# Use Alpine Linux Docker image
ccgo build linux --docker --libc musl --build-as static

Sanitizers

Enable address sanitizer for debugging:

[build]
cxxflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
ldflags = ["-fsanitize=address"]

Coverage Analysis

# Build with coverage
CXXFLAGS="-fprofile-arcs -ftest-coverage" ccgo build linux

# Run tests
ccgo test

# Generate report
gcov src/mylib.cpp
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory coverage_html

See Also