Skip to content
Merged
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Agent notes for Forge

Forge is a Lua-scriptable build tool. The C++ core builds a dependency graph of targets/rules/toolsets, binds targets to files, caches implicit dependencies and settings hashes, and rebuilds outdated targets based on timestamps/settings. Lua bindings and Lua rules provide the user-facing build DSL.

## Where to look

- `forge.lua` is the root build script for this repository.
- `src/forge/*.cpp` and `src/forge/*.hpp` are the core engine: `Forge`, `Graph`, `Target`, `Rule`, `Toolset`, scheduling, execution, graph I/O, and path functions.
- `src/forge/forge` is the CLI executable.
- `src/forge/forge_lua` exposes the C++ engine to Lua.
- `src/forge/forge_hooks` contains platform-specific filesystem access hooks used for automatic dependency detection.
- `src/forge/lua/forge` contains the standard Lua API/rules; `src/forge/lua/forge/cc` contains C/C++ toolchain rules.
- `src/forge/forge_test` contains Forge’s UnitTest++ test suite and Lua test fixtures.
- `src/assert`, `src/cmdline`, `src/error`, `src/luaxx`, and `src/process` are small local support libraries.
- `src/boost`, `src/lua`, and `src/unittest-cpp` are vendored/submodule code; avoid changing them unless explicitly needed.

## Docs map

- `docs/getting-started/running-forge.md` explains CLI commands and variables.
- `docs/getting-started/configuring-forge.md` explains root `forge.lua` build scripts and toolsets.
- `docs/getting-started/writing-buildfiles.md` explains `*.forge` buildfiles.
- `docs/more-details/rules.md`, `dependencies.md`, and `outdated-calculation.md` explain the core build model.
- `docs/reference/` documents Lua API functions, `Target`, and `Toolset`.
- `docs/cc-module/cc.md` documents the C/C++ Lua rules.

## Build and test

- Fresh checkout: run `git submodule update --init`, then `bash ./bootstrap-linux.bash`, `bash ./bootstrap-macos.bash`, or `bootstrap-windows.bat`.
- Normal build from repo root after bootstrapping: `bootstrap/bin/forge` or `bootstrap/bin/forge.exe`.
- Common variables/commands: `variant=debug|release|shipping`, `goal=path/to/target`, `clean`, `reconfigure`, `dependencies`, `namespace`.
- CI-style validation builds with `bootstrap/bin/forge`, installs release with `debug/bin/forge architecture=x86-64 variant=release prefix=forge install`, then runs `release/bin/forge_test`.

## Conventions and guardrails

- C++ is generally C++11; MSVC builds use C++14. Warnings are treated as errors.
- Follow surrounding C++ style and existing `sweet::forge` namespaces. Use Boost.Filesystem rather than introducing `std::filesystem`.
- Lua/buildfiles use 4-space indentation, semicolon-separated table entries, and portable `/` paths. Convert to native paths only when passing paths to external tools.
- Do not edit generated or ignored outputs such as `bootstrap/`, `debug/`, `release/`, `shipping/`, `.forge`, `*.o`, or `*.obj`.
- When changing behavior, check whether corresponding updates are needed in the C++ core, Lua bindings/rules, docs/reference, and `src/forge/forge_test`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
8 changes: 8 additions & 0 deletions docs/reference/system-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ function run( command, arguments, environment, dependencies_filter, stdout_filte

Executes `command` as for [`execute()`](#execute) but raises an error if the process exits with a non-zero exit code.

### shell

~~~lua
function shell( arguments, environment, dependencies_filter, stdout_filter, stderr_filter, ... );
~~~

Executes `arguments` in the systems native shell, i.e. `cmd.exe` on Windows, `bash` on Linux/MacOS. Raises an error if the shell process exits with a non-zero exit code.

### sleep

~~~lua
Expand Down
216 changes: 210 additions & 6 deletions src/error/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(BUILD_OS_MACOS) || defined(BUILD_OS_LINUX)
#include <signal.h>
#endif

using namespace sweet::error;

Expand Down Expand Up @@ -55,7 +58,7 @@ ErrorPolicy* get_error_policy()
// Handle an error.
//
// Passes the error on to the global ErrorPolicy if one has been set otherwise
// prints the error description to stderr and calls ::exit() passing
// prints the error description to stderr and calls ::exit() passing
// EXIT_FAILURE as the return code to pass back to the operating system.
//
// @param error
Expand All @@ -80,7 +83,7 @@ void error( const Error& error )
// Handle an error.
//
// Passes the error on to the global ErrorPolicy if one has been set otherwise
// prints the error description to stderr and calls ::exit() passing
// prints the error description to stderr and calls ::exit() passing
// EXIT_FAILURE as the return code to pass back to the operating system.
//
// @param exception
Expand All @@ -106,20 +109,20 @@ void error( const std::exception& exception )
//
// @param error
// The operating system error number.
//
//
// @param buffer
// A buffer to place the operating system error message into.
//
//
// @param length
// The length of the buffer.
//
//
// @return
// The buffer.
*/
const char* format( int error, char* buffer, unsigned int length )
{
SWEET_ASSERT( buffer );
#if defined(BUILD_OS_WINDOWS)
#if defined(BUILD_OS_WINDOWS)
int actual_length = ::FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, 0, error, 0, buffer, static_cast<int>(length), 0 );
while ( actual_length > 0 && (buffer[actual_length] == '\n' || buffer[actual_length] == '\r' || buffer[actual_length] == '.' || buffer[actual_length] == 0) )
{
Expand All @@ -136,6 +139,207 @@ const char* format( int error, char* buffer, unsigned int length )
return buffer;
}

/**
// Get the symbolic name of a POSIX signal.
//
// @param signal
// The signal number.
//
// @return
// The signal name (e.g. "SIGSEGV") or "unknown signal" if the signal is not
// recognised.
*/
const char* signal_name( int signal )
{
#if defined(BUILD_OS_MACOS) || defined(BUILD_OS_LINUX)
switch ( signal )
{
case SIGHUP:
return "SIGHUP";

case SIGINT:
return "SIGINT";

case SIGQUIT:
return "SIGQUIT";

case SIGILL:
return "SIGILL";

case SIGTRAP:
return "SIGTRAP";

case SIGABRT:
return "SIGABRT";

case SIGFPE:
return "SIGFPE";

case SIGKILL:
return "SIGKILL";

case SIGBUS:
return "SIGBUS";

case SIGSEGV:
return "SIGSEGV";

case SIGSYS:
return "SIGSYS";

case SIGPIPE:
return "SIGPIPE";

case SIGALRM:
return "SIGALRM";

case SIGTERM:
return "SIGTERM";

case SIGURG:
return "SIGURG";

case SIGSTOP:
return "SIGSTOP";

case SIGTSTP:
return "SIGTSTP";

case SIGCONT:
return "SIGCONT";

case SIGCHLD:
return "SIGCHLD";

case SIGTTIN:
return "SIGTTIN";

case SIGTTOU:
return "SIGTTOU";

case SIGXCPU:
return "SIGXCPU";

case SIGXFSZ:
return "SIGXFSZ";

case SIGVTALRM:
return "SIGVTALRM";

case SIGPROF:
return "SIGPROF";

case SIGWINCH:
return "SIGWINCH";

case SIGUSR1:
return "SIGUSR1";

case SIGUSR2:
return "SIGUSR2";

default:
return "unknown signal";
}
#else
(void) signal;
return "unknown signal";
#endif
}

/**
// Get the symbolic name of a Windows NTSTATUS exception code.
//
// @param code
// The NTSTATUS exception code.
//
// @return
// The exception name (e.g. "EXCEPTION_ACCESS_VIOLATION") or
// "unknown exception" if the code is not recognised.
*/
const char* exception_name( unsigned long code )
{
#if defined(BUILD_OS_WINDOWS)
switch ( code )
{
case EXCEPTION_ACCESS_VIOLATION:
return "EXCEPTION_ACCESS_VIOLATION";

case EXCEPTION_DATATYPE_MISALIGNMENT:
return "EXCEPTION_DATATYPE_MISALIGNMENT";

case EXCEPTION_BREAKPOINT:
return "EXCEPTION_BREAKPOINT";

case EXCEPTION_SINGLE_STEP:
return "EXCEPTION_SINGLE_STEP";

case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";

case EXCEPTION_FLT_DENORMAL_OPERAND:
return "EXCEPTION_FLT_DENORMAL_OPERAND";

case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";

case EXCEPTION_FLT_INEXACT_RESULT:
return "EXCEPTION_FLT_INEXACT_RESULT";

case EXCEPTION_FLT_INVALID_OPERATION:
return "EXCEPTION_FLT_INVALID_OPERATION";

case EXCEPTION_FLT_OVERFLOW:
return "EXCEPTION_FLT_OVERFLOW";

case EXCEPTION_FLT_STACK_CHECK:
return "EXCEPTION_FLT_STACK_CHECK";

case EXCEPTION_FLT_UNDERFLOW:
return "EXCEPTION_FLT_UNDERFLOW";

case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "EXCEPTION_INT_DIVIDE_BY_ZERO";

case EXCEPTION_INT_OVERFLOW:
return "EXCEPTION_INT_OVERFLOW";

case EXCEPTION_PRIV_INSTRUCTION:
return "EXCEPTION_PRIV_INSTRUCTION";

case EXCEPTION_IN_PAGE_ERROR:
return "EXCEPTION_IN_PAGE_ERROR";

case EXCEPTION_ILLEGAL_INSTRUCTION:
return "EXCEPTION_ILLEGAL_INSTRUCTION";

case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";

case EXCEPTION_STACK_OVERFLOW:
return "EXCEPTION_STACK_OVERFLOW";

case EXCEPTION_INVALID_DISPOSITION:
return "EXCEPTION_INVALID_DISPOSITION";

case EXCEPTION_GUARD_PAGE:
return "EXCEPTION_GUARD_PAGE";

case EXCEPTION_INVALID_HANDLE:
return "EXCEPTION_INVALID_HANDLE";

case STATUS_CONTROL_C_EXIT:
return "STATUS_CONTROL_C_EXIT";

default:
return "unknown exception";
}
#else
(void) code;
return "unknown exception";
#endif
}

}

}
2 changes: 2 additions & 0 deletions src/error/functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ ErrorPolicy* get_error_policy();
void error( const Error& error );
void error( const std::exception& exception );
const char* format( int error, char* buffer, unsigned int length );
const char* signal_name( int signal );
const char* exception_name( unsigned long code );

}

Expand Down
15 changes: 9 additions & 6 deletions src/forge/Executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,18 @@ void Executor::stop()
{
{
std::unique_lock<std::mutex> lock( jobs_mutex_ );
if ( !jobs_.empty() )
while ( !jobs_.empty() )
{
jobs_empty_condition_.wait( lock );
}
done_ = true;
jobs_ready_condition_.notify_all();
}

for ( vector<std::thread*>::iterator i = threads_.begin(); i != threads_.end(); ++i )
vector<std::thread*> threads;
threads.swap( threads_ );

for ( vector<std::thread*>::iterator i = threads.begin(); i != threads.end(); ++i )
{
try
{
Expand All @@ -201,11 +204,11 @@ void Executor::stop()
forge_->errorf( "Failed to join thread - %s", exception.what() );
}
}
while ( !threads_.empty() )

while ( !threads.empty() )
{
delete threads_.back();
threads_.pop_back();
delete threads.back();
threads.pop_back();
}
}
}
Expand Down
Loading
Loading