diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..69c6ec01 --- /dev/null +++ b/AGENTS.md @@ -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`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/docs/reference/system-functions.md b/docs/reference/system-functions.md index 47f2dcfa..ee2ab772 100644 --- a/docs/reference/system-functions.md +++ b/docs/reference/system-functions.md @@ -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 diff --git a/src/error/functions.cpp b/src/error/functions.cpp index e72e8788..799e5f0a 100644 --- a/src/error/functions.cpp +++ b/src/error/functions.cpp @@ -12,6 +12,9 @@ #include #include #include +#if defined(BUILD_OS_MACOS) || defined(BUILD_OS_LINUX) +#include +#endif using namespace sweet::error; @@ -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 @@ -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 @@ -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(length), 0 ); while ( actual_length > 0 && (buffer[actual_length] == '\n' || buffer[actual_length] == '\r' || buffer[actual_length] == '.' || buffer[actual_length] == 0) ) { @@ -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 +} + } } diff --git a/src/error/functions.hpp b/src/error/functions.hpp index 1fc8c825..5069de68 100644 --- a/src/error/functions.hpp +++ b/src/error/functions.hpp @@ -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 ); } diff --git a/src/forge/Executor.cpp b/src/forge/Executor.cpp index f30f695f..da9952e5 100644 --- a/src/forge/Executor.cpp +++ b/src/forge/Executor.cpp @@ -179,7 +179,7 @@ void Executor::stop() { { std::unique_lock lock( jobs_mutex_ ); - if ( !jobs_.empty() ) + while ( !jobs_.empty() ) { jobs_empty_condition_.wait( lock ); } @@ -187,7 +187,10 @@ void Executor::stop() jobs_ready_condition_.notify_all(); } - for ( vector::iterator i = threads_.begin(); i != threads_.end(); ++i ) + vector threads; + threads.swap( threads_ ); + + for ( vector::iterator i = threads.begin(); i != threads.end(); ++i ) { try { @@ -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(); } } } diff --git a/src/forge/Reader.cpp b/src/forge/Reader.cpp index 5d8625a3..fd2267fe 100644 --- a/src/forge/Reader.cpp +++ b/src/forge/Reader.cpp @@ -143,7 +143,7 @@ void Reader::stop() { { std::unique_lock lock( jobs_mutex_ ); - if ( !jobs_.empty() ) + while ( !jobs_.empty() ) { jobs_empty_condition_.wait( lock ); } @@ -151,7 +151,10 @@ void Reader::stop() jobs_ready_condition_.notify_all(); } - for ( vector::iterator i = threads_.begin(); i != threads_.end(); ++i ) + vector threads; + threads.swap( threads_ ); + + for ( vector::iterator i = threads.begin(); i != threads.end(); ++i ) { try { @@ -165,11 +168,11 @@ void Reader::stop() forge_->errorf( 0, "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(); } } } diff --git a/src/forge/System.cpp b/src/forge/System.cpp index ae1f8d0a..59832da5 100644 --- a/src/forge/System.cpp +++ b/src/forge/System.cpp @@ -115,8 +115,8 @@ std::time_t System::last_write_time( const std::string& path ) const */ boost::filesystem::directory_iterator System::ls( const std::string& path ) const { - return boost::filesystem::exists( path ) ? - boost::filesystem::directory_iterator( path ) : + return boost::filesystem::exists( path ) ? + boost::filesystem::directory_iterator( path ) : boost::filesystem::directory_iterator() ; } @@ -128,13 +128,13 @@ boost::filesystem::directory_iterator System::ls( const std::string& path ) cons // The directory to list files in. // // @return -// A boost::filesystem::recursive_directory_iterator that recursively +// A boost::filesystem::recursive_directory_iterator that recursively // iterates over the files in the directory and its children. */ boost::filesystem::recursive_directory_iterator System::find( const std::string& path ) const { - return boost::filesystem::exists( path ) ? - boost::filesystem::recursive_directory_iterator( path ) : + return boost::filesystem::exists( path ) ? + boost::filesystem::recursive_directory_iterator( path ) : boost::filesystem::recursive_directory_iterator() ; } @@ -160,8 +160,8 @@ std::string System::executable() const file = INVALID_HANDLE_VALUE; if ( linked_size < sizeof(linked_path) ) { - // The path returned by `::GetFinalPathNameByHandleA()` has a - // prefix of `\\?\` to indicate that it is an extended length + // The path returned by `::GetFinalPathNameByHandleA()` has a + // prefix of `\\?\` to indicate that it is an extended length // path. Skipping over it works for now but is probably the wrong // thing in many cases. const char* linked_path_without_extended_length_prefix = linked_path + 4; @@ -177,7 +177,7 @@ std::string System::executable() const int linked_size = readlink( executable_path, linked_path, sizeof(linked_path) - 1 ); if ( linked_size == -1 && errno == EINVAL ) { - return boost::filesystem::path( string(executable_path, size) ).generic_string(); + return boost::filesystem::path( string(executable_path) ).generic_string(); } else if ( linked_size >= 0 ) { @@ -213,7 +213,7 @@ std::string System::home() const #else #error "ScriptInterface::home() is not implemented for this platform" #endif - + const char* home = ::getenv( HOME ); return home ? boost::filesystem::path( string(home) ).generic_string() : string(); } @@ -291,7 +291,7 @@ const char* System::operating_system() const // The name of the environment attribute to get the value of. // // @return -// The value of the environment attribute or null if the environment attribute +// The value of the environment attribute or null if the environment attribute // isn't set. */ const char* System::getenv( const char* name ) const @@ -353,7 +353,7 @@ void System::sleep( float milliseconds ) const // The number of milliseconds elapsed since the system was started. */ float System::ticks() const -{ +{ #if defined(BUILD_OS_WINDOWS) return static_cast( ::GetTickCount() ) - initial_tick_count_; #elif defined(BUILD_OS_MACOS) diff --git a/src/forge/forge_hooks/forge_hooks_macos.cpp b/src/forge/forge_hooks/forge_hooks_macos.cpp index 4a3e9315..351d0e37 100644 --- a/src/forge/forge_hooks/forge_hooks_macos.cpp +++ b/src/forge/forge_hooks/forge_hooks_macos.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -23,7 +22,7 @@ static void log_open( int fd, int oflag ) if ( fd >= 0 ) { struct stat stat; - if ( fstat(fd, &stat) == 0 && (stat.st_mode & S_IFREG) != 0 ) + if ( fstat(fd, &stat) == 0 && (stat.st_mode & S_IFREG) != 0 ) { char path [PATH_MAX]; int result = fcntl( fd, F_GETPATH, path ); @@ -59,7 +58,7 @@ static void log_open( int fd, int oflag ) } -extern "C" +extern "C" { int open_interpose( const char* filename, int oflag, ... ) diff --git a/src/forge/lua/forge/cc/gcc.lua b/src/forge/lua/forge/cc/gcc.lua index 91449a05..0384ef8a 100644 --- a/src/forge/lua/forge/cc/gcc.lua +++ b/src/forge/lua/forge/cc/gcc.lua @@ -261,7 +261,11 @@ function gcc.append_link_flags(toolset, target, flags) gcc.append_flags(flags, target.ldflags); table.insert(flags, ('-march=%s'):format(toolset.architecture)); - table.insert(flags, "-std=c++11"); + + local standard = toolset.standard or target.standard; + if standard then + table.insert(flags, ("-std=%s"):format(standard)); + end if target:rule() == toolset.DynamicLibrary then table.insert(flags, "-shared"); diff --git a/src/forge/lua/forge/init.lua b/src/forge/lua/forge/init.lua index 97915b7e..aba619d2 100644 --- a/src/forge/lua/forge/init.lua +++ b/src/forge/lua/forge/init.lua @@ -157,25 +157,26 @@ function run(command, arguments, environment, dependencies_filter, stdout_filter if type(arguments) == 'table' then arguments = table.concat(arguments, ' '); end - if execute(command, arguments, environment, dependencies_filter, stdout_filter, stderr_filter, ...) ~= 0 then - error(('%s failed'):format(arguments), 0); + local result = execute(command, arguments, environment, dependencies_filter, stdout_filter, stderr_filter, ...); + if result ~= 0 then + error(('[[%s]] failed with exit code (%d)'):format(arguments, result), 0); end end -- Execute a command through the host system's native shell - either -- "C:/windows/system32/cmd.exe" on Windows system or "/bin/sh" anywhere else. -function shell(arguments, dependencies_filter, stdout_filter, stderr_filter, ...) +function shell(arguments, environment, dependencies_filter, stdout_filter, stderr_filter, ...) if type(arguments) == 'table' then arguments = table.concat(arguments, ' '); end if operating_system() == 'windows' then local cmd = 'C:/windows/system32/cmd.exe'; - local result = execute(cmd, ('cmd /c "%s"'):format(arguments), dependencies_filter, stdout_filter, stderr_filter, ...); - assertf(result == 0, '[[%s]] failed (result=%d)', arguments, result); + local result = execute(cmd, ('cmd /c "%s"'):format(arguments), environment, dependencies_filter, stdout_filter, stderr_filter, ...); + assertf(result == 0, '[[%s]] failed with exit code (%d)', arguments, result); else local sh = '/bin/sh'; - local result = execute(sh, ('sh -c "%s"'):format(arguments), dependencies_filter, stdout_filter, stderr_filter, ...); - assertf(result == 0, '[[%s]] failed (result=%d)', arguments, tonumber(result)); + local result = execute(sh, ('sh -c "%s"'):format(arguments), environment, dependencies_filter, stdout_filter, stderr_filter, ...); + assertf(result == 0, '[[%s]] failed with exit code (%d)', arguments, tonumber(result)); end end diff --git a/src/process/Process.cpp b/src/process/Process.cpp index 870189fb..eb9026cc 100644 --- a/src/process/Process.cpp +++ b/src/process/Process.cpp @@ -8,6 +8,7 @@ #include "Process.hpp" #include "Environment.hpp" #include "Error.hpp" +#include #include #if defined(BUILD_OS_WINDOWS) @@ -45,7 +46,8 @@ Process::Process() pipes_(), #if defined(BUILD_OS_WINDOWS) process_( INVALID_HANDLE_VALUE ), - suspended_thread_( INVALID_HANDLE_VALUE ) + suspended_thread_( INVALID_HANDLE_VALUE ), + exit_code_( 0 ) #elif defined(BUILD_OS_MACOS) process_( 0 ), exit_code_( 0 ), @@ -64,7 +66,7 @@ Process::~Process() { resume(); -#if defined(BUILD_OS_WINDOWS) +#if defined(BUILD_OS_WINDOWS) if ( suspended_thread_ != INVALID_HANDLE_VALUE ) { ::CloseHandle( suspended_thread_ ); @@ -282,9 +284,9 @@ void Process::run( const char* arguments ) ::CloseHandle( process_information.hThread ); } - // Close the the write ends of any the pipes used because they only need + // Close the the write ends of any the pipes used because they only need // to be used by the child process and also because calls to ::ReadFile() - // on the read end only return 0 when all of the write ends have been + // on the read end only return 0 when all of the write ends have been // closed. for ( vector::iterator pipe = pipes_.begin(); pipe != pipes_.end(); ++pipe ) { @@ -302,9 +304,9 @@ void Process::run( const char* arguments ) if ( directory_ ) { - // Use the undocumented `pthread_fchdir()` system call to change the + // Use the undocumented `pthread_fchdir()` system call to change the // working directory for the thread that is spawning a process rather than - // the global per-process working directory changed by `fchdir()`. See + // the global per-process working directory changed by `fchdir()`. See // `syscall()` and ``. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -474,7 +476,7 @@ void Process::run( const char* arguments ) int result = execve( executable_, &splitter.arguments()[0], envp ); - // Ignore any returned result as any call to `execve()` that returns + // Ignore any returned result as any call to `execve()` that returns // is a failure. (void) result; char message [256]; @@ -490,13 +492,13 @@ void Process::run( const char* arguments ) pipe->write_fd = -1; } } -#endif +#endif } /** // Get the handle or identifier of this Process. // -// @return +// @return // The handle or identifier of this Process cast to a void pointer. */ void* Process::process() const @@ -533,7 +535,7 @@ void* Process::write_pipe( int index ) const /** // Resume this Process after it has been started suspended. // -// It is only valid to call this function once to resume an initially +// It is only valid to call this function once to resume an initially // suspended process. Additional calls will silently do nothing. */ void Process::resume() @@ -570,21 +572,27 @@ void Process::wait() SWEET_ERROR( WaitForProcessFailedError("Waiting for a process failed - %s", error) ); } -#elif defined(BUILD_OS_MACOS) || defined(BUILD_OS_LINUX) - SWEET_ASSERT( process_ != 0 ); - - pid_t result = waitpid( process_, &exit_code_, 0 ); - while ( result < 0 && errno == EINTR ) + DWORD exit_code = 0; + BOOL exited = ::GetExitCodeProcess( process_, &exit_code ); + if ( !exited ) { - result = waitpid( process_, &exit_code_, 0 ); + char error [1024]; + error::Error::format( ::GetLastError(), error, sizeof(error) ); + SWEET_ERROR( ExitCodeForProcessFailedError("Getting a process exit code failed - %s", error) ); } - if ( result != process_ ) + exit_code_ = (int) exit_code; + + ::CloseHandle( process_ ); + process_ = INVALID_HANDLE_VALUE; + + const DWORD NT_STATUS_SEVERITY_ERROR_MASK = (DWORD) 0xc0000000; + if ( (exit_code & NT_STATUS_SEVERITY_ERROR_MASK) == NT_STATUS_SEVERITY_ERROR_MASK ) { - char buffer [1024]; - SWEET_ERROR( WaitForProcessFailedError("Waiting for a process failed - %s", Error::format(errno, buffer, sizeof(buffer))) ); + SWEET_ERROR( WaitForProcessFailedError("Process terminated by exception %s (0x%08lx)", error::exception_name(exit_code), (unsigned long) exit_code) ); + return; } - process_ = 0; -#elif defined(BUILD_OS_LINUX) + +#elif defined(BUILD_OS_MACOS) || defined(BUILD_OS_LINUX) SWEET_ASSERT( process_ != 0 ); int status = 0; @@ -594,25 +602,26 @@ void Process::wait() result = waitpid( process_, &status, 0 ); } - if ( result == process_ ) - { - if ( WIFEXITED(status) ) - { - exit_code_ = WEXITSTATUS( status ); - } - else if ( WIFSIGNALED(status) ) - { - exit_code_ = WTERMSIG( status ); - } - } - else + if ( result != process_ ) { char buffer [1024]; SWEET_ERROR( WaitForProcessFailedError("Waiting for a process failed - %s", Error::format(errno, buffer, sizeof(buffer))) ); return; } - process_ = 0; + if ( WIFSIGNALED(status) ) + { + int signal = WTERMSIG(status); + SWEET_ERROR( WaitForProcessFailedError("Process terminated by signal %s (%d)", error::signal_name(signal), signal) ); + return; + } + + if ( WIFEXITED(status) ) + { + exit_code_ = WEXITSTATUS( status ); + } + + process_ = 0; #endif } @@ -625,26 +634,9 @@ void Process::wait() int Process::exit_code() { #if defined(BUILD_OS_WINDOWS) - SWEET_ASSERT( process_ != INVALID_HANDLE_VALUE ); - - DWORD exit_code = 0; - BOOL exited = ::GetExitCodeProcess( process_, &exit_code ); - if ( !exited ) - { - char error [1024]; - error::Error::format( ::GetLastError(), error, sizeof(error) ); - SWEET_ERROR( ExitCodeForProcessFailedError("Getting a process exit code failed - %s", error) ); - } - - if ( process_ != INVALID_HANDLE_VALUE ) - { - ::CloseHandle( process_ ); - process_ = INVALID_HANDLE_VALUE; - } - - return exit_code; + SWEET_ASSERT( process_ == INVALID_HANDLE_VALUE ); #elif defined(BUILD_OS_MACOS) || defined(BUILD_OS_LINUX) SWEET_ASSERT( process_ == 0 ); - return exit_code_; #endif + return exit_code_; } diff --git a/src/process/Process.hpp b/src/process/Process.hpp index e6f51d8f..d24fe4b6 100644 --- a/src/process/Process.hpp +++ b/src/process/Process.hpp @@ -44,18 +44,16 @@ class Process bool start_suspended_; bool inherit_environment_; std::vector pipes_; - #if defined(BUILD_OS_WINDOWS) void* process_; ///< The handle to this Process. void* suspended_thread_; ///< The handle to the suspended main thread of this Process. + int exit_code_; #endif - #if defined(BUILD_OS_MACOS) pid_t process_; int exit_code_; bool suspended_; #endif - #if defined(BUILD_OS_LINUX) pid_t process_; int exit_code_; diff --git a/src/process/process.forge b/src/process/process.forge index b45cca2d..84967257 100644 --- a/src/process/process.forge +++ b/src/process/process.forge @@ -5,6 +5,7 @@ for _, forge in toolsets('cc.*') do forge:StaticLibrary '${lib}/process_${architecture}' { '${lib}/assert_${architecture}'; '${lib}/cmdline_${architecture}'; + '${lib}/error_${architecture}'; forge:Cxx '${obj}/%1' { 'Error.cpp', 'Environment.cpp',