Skip to content

Dark-CodeX/runcpp

Repository files navigation

runcpp: An Easy-to-use Build System

runcpp is an open-source build automation tool designed to simplify the process of generating executables and other non-source files from source code. Written in C++, it provides a fast, memory-safe, and cross-platform solution for managing your build process with a simple and intuitive syntax.

The tool relies on a custom-written parser and process creation mechanisms, leveraging the openutils library to ensure memory safety without leaks or invalid memory access. It is compatible with Windows, Linux, and macOS, using native APIs (windows.h on Windows, fork() and execvp() on Unix-like systems) for optimal performance.

Features

  • Simple Syntax: A user-friendly configuration syntax that is easy to read and maintain.
  • Cross-Platform: Seamlessly works on Windows, Linux, and macOS.
  • Memory Safe: Built to prevent invalid memory access and leaks.
  • Fast & Optimized: Custom parser and native process calls ensure high performance.
  • Target-Based Builds: Define and call specific build targets.
  • Dependency Checking: Use the depends keyword to verify the existence of tools, files, or directories.
  • Conditional Compilation: Use if-else statements based on OS or other variables.
  • Shell Command Integration: Capture and use the output of shell commands within your build script using shell.
  • File Imports: Structure your build logic by importing other configuration files.
  • Serialization: Speed up execution by pre-parsing and serializing configuration files into a faster binary format.

Building from Source

To build runcpp from source, you will need a C++20 compliant compiler (like GCC, Clang, or MSVC) and the openutils library headers available in your include path.

  1. Clone the repository:

    git clone https://github.com/Dark-CodeX/runcpp.git
    cd runcpp
  2. Compile the source code: runcpp uses itself for building. For the initial build, you can compile all source files manually.

    On Linux or macOS (with g++ or clang++):

    mkdir -p bin
    g++ -std=c++20 -O3 -o ./bin/runcpp runcpp/src/**/*.cc

    Note: The ** glob might require a specific shell or settings. If it fails, you'll need to list the .cc files manually.

    On Windows (with MSVC):

    cl.exe /std:c++latest /O2 /EHsc /Fe:./bin/runcpp.exe ./runcpp/src/**/*.cc
  3. (Optional) Install runcpp: After building, you can move the executable to a directory in your system's PATH.

    On Linux or macOS:

    sudo mv ./bin/runcpp /usr/local/bin/

Usage

runcpp by default looks for a compile.rc file in the current directory and executes the all target.

# Runs the 'all' target from ./compile.rc
runcpp

# Runs the 'release' and 'debug' targets from ./compile.rc
runcpp release debug

Command-Line Options

Flag Description
-h, --help Display the help message.
-v, --version Display the version of runcpp.
-gen, --generate Interactively generate a basic compile.rc configuration file.
-f, --file <path> Specify a configuration file to use instead of the default ./compile.rc.
-p, --print Print the parsed configuration to the console before executing. Useful for debugging.
-s, --serialize <in> Serialize a configuration file into a binary format. Use with -o <out> to specify the output file.
-d, --deserialize <in> Execute targets from a serialized binary configuration file.
-m, --merge <in> Resolve all import statements and merge configurations into a single output file. Use with -o <out>.
-i, --init Start runcpp in interactive mode.

Interactive Mode

Start an interactive runcpp session where you can run commands repeatedly.

$ runcpp --init
/> release
# Executes the release target
/> --file ./test/build.rc clean
# Executes 'clean' target from './test/build.rc'
/> --exit
$

Configuration (compile.rc) Syntax

The configuration file uses a simple, target-oriented syntax.

Targets and Variables

Targets are defined with [target_name]:. Variables and commands are defined within the target. Variables can hold a single string or an array of strings.

# This is a comment
[compiler]:
    # Define a variable 'cc' based on the operating system
    if os == 'windows'
        cc = 'cl.exe'
    else
        cc = 'g++'
    endif

[sources]:
    src_files = ['./src/main.cc', './src/utils.cc']

[release]:
    # Call other targets to get their values
    compiler()
    sources()
    # Execute a command
    command = [cc()[0], '-O3', src_files(), '-o', 'myapp']

Conditionals

Use if, else, and endif for conditional logic. The primary built-in variable is os, which can be 'windows', 'linux', 'mac', or 'unix'.

[arguments]:
    if os == 'windows'
        release_args = ['/std:c++latest', '/O2']
    else
        release_args = ['-std=c++20', '-O3', '-flto']
    endif

Target Calling and Indexing

Call another target using target_name(). You can access specific elements from the returned array using index notation [index].

[output_files]:
    files = ['app_release', 'app_debug']

[build_release]:
    # Access the first element ('app_release') from the 'output_files' target
    cmd = ['g++', '-o', output_files()[0]]

Dependencies

Check for the existence of commands, files, or directories using depends(). The build will fail if a dependency is not found.

# Check if g++ command is available in PATH
depends('g++')

# Check if a header file exists
depends('./src/header.hh')

# Check if a directory exists
depends('./bin')

Imports

Include other configuration files using import. Paths are relative to the current file.

# Import shared settings
import "./config/common.rc"

[release]:
    common_args() # Target defined in common.rc
    # ... release specific commands

Shell Commands

Execute a shell command and use its standard output as a variable's value using shell().

[cflags]:
    # Get compiler flags from pkg-config
    flags = shell('pkg-config --cflags my-library')

[compile]:
    cflags()
    cmd = ['g++', flags(), 'main.cc']

Contributing

Contributions are welcome! Please read the CONTRIBUTING.md file for guidelines on how to report bugs, suggest enhancements, and submit pull requests.

Key rules include:

  • Use C-style I/O (printf, FILE*) instead of C++ streams.
  • Ensure no memory leaks are introduced.
  • Format your code using the provided .clang-format file.

License

This project is licensed under the GNU General Public License v3.0. See the LICENSE file for more details.

Releases

Contributors

Languages