diff --git a/.gitattributes b/.gitattributes index 76a2f865..1ad0590c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,11 @@ # Sources: GitHub Docs (line endings), git-scm gitattributes (diff=cpp), # gitattributes/gitattributes C++.gitattributes + Common.gitattributes, # github-linguist overrides. +# +# Linguist language-bar policy (Synesis): classify public C API headers as C +# and *.cmake as CMake scripts; keep helper scripts, IDE project files, and +# CMakeLists.txt config out of the advertised language mix. Prefer +# -linguist-detectable over linguist-detectable=false. # Default: detect text, normalize to LF in the repository * text=auto @@ -97,3 +102,25 @@ TODO text # --- GitHub Linguist --- **/build/** linguist-generated **/cmake-build-*/** linguist-generated + +# Classification (C-primary / C API with C++ tests; *.cmake as CMake scripts) +*.c linguist-language=C +*.cmake linguist-language=CMake +*.cpp linguist-language=C++ +*.cxx linguist-language=C++ +*.h linguist-language=C +*.hpp linguist-language=C++ +*.hxx linguist-language=C++ + +# Keep the bar on library languages (helpers / legacy IDE / docs / +# CMakeLists.txt config; leave *.cmake detectable as CMake) +*.cmd -linguist-detectable +*.dsp -linguist-detectable +*.dsw -linguist-detectable +*.html -linguist-detectable +*.sh -linguist-detectable +*.sln -linguist-detectable +*.vcxproj -linguist-detectable +*.vimrc -linguist-detectable +CMakeLists.txt -linguist-detectable +makefile -linguist-detectable diff --git a/.github/workflows/ci-cell.yml b/.github/workflows/ci-cell.yml index acac2798..0a9c3979 100644 --- a/.github/workflows/ci-cell.yml +++ b/.github/workflows/ci-cell.yml @@ -478,7 +478,7 @@ jobs: if [ "${{ inputs.c-compiler }}" = "mingw" ]; then export PATH="/mingw64/bin:$PATH" fi - SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_unit_tests.sh -M --component-only + SIS_CMAKE_BUILD_DIR="${{ github.workspace }}/build" ./run_all_component_tests.sh -M test-scratch: name: Scratch tests (${{ inputs.cell-id }}) diff --git a/CHANGES.md b/CHANGES.md index c9fee270..45a2339e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,39 @@ # STLSoft - CHANGES +## 1.11.1-rc8 - 10th October 2026 + +* additions: + * Added `stlsoft_C_environment_variable_strtoll_m()` and `stlsoft::environment_variable_strtoll()`, with UNIX `getenv()` / `strtoll()` and Windows `getenv_s()` / `STLSOFT_C_AUTO_BUFFER` support; added component tests; + * Added `unixstl_C_get_current_process_id()` (**unixstl/process/functions.h**, new), `winstl_C_get_current_process_id()` (**winstl/process/functions.h**), and `platformstl_C_get_current_process_id()` (**platformstl/process/functions.h**, new), in terms of `getpid()` / `_getpid()`; + * **unixstl::filesystem_traits** — added `rewind_dir()` (wrapping `rewinddir()`) to complement `open_dir()`, `read_dir()`, and `close_dir()`, so that a directory search may be restarted from its first entry without closing and reopening the handle; +* fixes: + * Fixed `stlsoft_C_environment_variable_exists_m()` when `getenv_s()` reports an existing empty variable; added component tests for `stlsoft::environment_variable_exists()`; + * **unixstl::filesystem_traits** — `class_type` is now defined in terms of `char_type` (the wide-character specialisation previously named the narrow specialisation); + * **unixstl** / **winstl** `system_traits` and `filesystem_traits` — canonicalised the conditional-compilation guards (no behavioural change); +* testing: + * Component tests for `unixstl::filesystem_traits` (**test/component/unixstl/filesystem/filesystem_traits/opendir**) cover `open_dir()`, `read_dir()`, `rewind_dir()` (before any read, after a partial read, after exhaustion, on an empty directory, and repeatedly), and `close_dir()`; + * Scratch test **test/scratch/unixstl/filesystem/rewind_dir** — exercises `rewind_dir()` over a directory given on the command-line; + * Example **examples/by_project/unixstl/filesystem/rewind_dir** — enumerates a directory in two passes (count, then collect), using `rewind_dir()` between them; + * **test.unit.versions** — aligned with **1.11.1-rc8** (**_STLSOFT_VER_1_11_1_RC8**); +* version: + * **unixstl** — **1.8.6 beta 7** (**_UNIXSTL_VER_1_8_6_B07**), with **STLSoft 1.11.1-rc8**; + * **winstl** — **1.13.0 beta 4** (**_WINSTL_VER_1_13_0_B04**), with **STLSoft 1.11.1-rc8**; + * **inetstl** — **1.5.0 alpha 3** (**_INETSTL_VER_1_5_0_A03**), with **STLSoft 1.11.1-rc8**; +* boilerplate: + * Added **run_all_component_tests.sh** / **.cmd**, **run_all_performance_tests.sh** / **.cmd** (with `--gap-groups`), and **run_all_automated_tests.sh** / **.cmd** (unit then component; `--unit-only` / `--component-only`); + * Added **generate_doxygen.sh**, **Doxyfile**, and **doc/mainpage.md** — HTML API documentation generated from the public headers into **_build/doxygen/html**; + * **remove_cmake_artefacts.sh** — no longer removes **docs** or **Doxyfile**; iterates arrays safely; + * **batch_compile_check**, **execute_performance_tests.sh** — quote the `$(dirname ...)` directory resolution; + * Scripts — **build_cmake.sh**, **clean_cmake.sh**, **ctest_cmake.sh**, **prepare_cmake.sh**, **remove_cmake_artefacts.sh**, **run_all_examples.sh**, and **run_all_scratch_tests.sh** rewritten to the common Synesis helper-script layout: builds go through `cmake --build` (with `--config` selected from `SIS_CMAKE_CONFIG` for multi-config generators) rather than `make` / `mingw32-make`; coloured output (`-A` / `--always-use-colours`, `SIS_CMAKE_ALWAYS_USE_COLOURS`, `NO_COLOR`); quoted paths and arguments; `.sis/project_name.txt` used in all status messages; + * **prepare_cmake.sh** — added `--build-shared-libs` and `-s` / `--stlsoft-root-dir`; honours `SIS_CMAKE_GENERATOR` and `SIS_CMAKE_MINGW`; every `-D` option is passed on all generator paths; + * **run_all_unit_tests.sh** / **.cmd** — now run unit-test programs only (`--unit-only` accepted for compatibility; `--component-only` removed); + * **run_all_examples.sh** — retains `--skip-interactive` (**.github/ci_skip_interactive_examples.txt**, which prevents headless CI hanging on GUI examples) and its discovery of every executable under the **examples** tree, and now exports `SIS_EXAMPLE_SMOKE=1`; **run_all_scratch_tests.sh** / **.cmd** — removed `--verbosity`; + * **.gitattributes** — Linguist language-bar policy: public C headers and sources are classified as C, C++ headers and sources as C++, and `*.cmake` as CMake; helper scripts, IDE project files, **CMakeLists.txt**, and **makefile** are excluded from the advertised language mix (`-linguist-detectable`); + * CI — **ci-cell.yml** runs component tests via **run_all_component_tests.sh**; + * **INSTALL.md** — documented **run_all_component_tests.sh**, **run_all_automated_tests.sh**, and **generate_doxygen.sh**; corrected the unit-test discovery patterns; + + ## 1.11.1-rc7 - 30th September 2026 * **simple_string** — counted construction, `assign`, `append`, `push_back`, and concatenation keep an embedded NUL, including assignment of a single `'\0'`; diff --git a/CMakeLists.txt b/CMakeLists.txt index 93f51914..f8d841ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ # Purpose: Top-level CMake lists file for STLSoft # # Created: 31st March 2021 -# Updated: 30th September 2026 +# Updated: 10th October 2026 # # ######################################################################## # diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..2b908973 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,72 @@ +# Doxyfile for STLSoft (C/C++ API; 100% header-only) + +PROJECT_NAME = "STLSoft" +PROJECT_BRIEF = "Robust, lightweight, cross-platform, template software" +PROJECT_NUMBER = 1.11.1-rc8 + +# Overridden at generation time by generate_doxygen.sh (respects +# SIS_CMAKE_BUILD_DIR). +OUTPUT_DIRECTORY = _build/doxygen +CREATE_SUBDIRS = NO + +OUTPUT_LANGUAGE = English +TAB_SIZE = 4 +# Many header names recur across sub-projects (e.g. **functions.h**), so +# retain the path relative to include/. +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = include +STRIP_FROM_INC_PATH = include +TIMESTAMP = NO + +INPUT = include \ + doc +FILE_PATTERNS = *.h \ + *.hpp \ + *.md +RECURSIVE = YES + +USE_MDFILE_AS_MAINPAGE = doc/mainpage.md + +EXAMPLE_PATH = examples +EXAMPLE_RECURSIVE = YES + +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO + +GENERATE_HTML = YES +GENERATE_LATEX = NO +HTML_OUTPUT = html + +GENERATE_TREEVIEW = YES +DISABLE_INDEX = NO +SEARCHENGINE = YES +SOURCE_BROWSER = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES + +MARKDOWN_SUPPORT = YES +JAVADOC_AUTOBRIEF = YES +QUIET = YES + +# Undocumented-member and parameter warnings are disabled (unlike the +# smaller C libraries) because the very large template corpus would bury +# genuine documentation errors. +WARN_IF_UNDOCUMENTED = NO +WARN_NO_PARAMDOC = NO +WARN_IF_DOC_ERROR = YES + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SKIP_FUNCTION_MACROS = YES +# Skip internal version/edit noise. +PREDEFINED = STLSOFT_DOCUMENTATION_SKIP_SECTION + +HAVE_DOT = NO + + +# ########################### end of file ########################### # diff --git a/EXAMPLES.md b/EXAMPLES.md index e12dae08..e5104e6a 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -15,6 +15,7 @@ | **glob_sequence** | [by_project/unixstl/filesystem/glob_sequence](./examples/by_project/unixstl/filesystem/glob_sequence/main.cpp) | Unix | List selected filesystem entries with `unixstl::glob_sequence` | | **home_directory** | [by_project/unixstl/system/home_directory](./examples/by_project/unixstl/system/home_directory/main.cpp) | Unix | Illustrates use of `unixstl::home_directory` | | **readdir_sequence** | [by_project/unixstl/filesystem/readdir_sequence](./examples/by_project/unixstl/filesystem/readdir_sequence/main.cpp) | Unix | List filesystem entries with `unixstl::readdir_sequence` | +| **rewind_dir** | [by_project/unixstl/filesystem/rewind_dir](./examples/by_project/unixstl/filesystem/rewind_dir/main.cpp) | Unix | Enumerate a directory twice with `unixstl::filesystem_traits<>::rewind_dir()` | | | | | | | **C.output_debug_line** | [by_project/winstl/diagnostics/C.output_debug_line](./examples/by_project/winstl/diagnostics/C.output_debug_line/main.c) | Windows | Emit debug strings with the C API `winstl_C_diagnostics_output_debug_line_*` | | **codepages** | [by_project/winstl/i18n/codepages](./examples/by_project/winstl/i18n/codepages/main.cpp) | Windows | Enumerate system code pages with `winstl::codepage_sequence` | diff --git a/INSTALL.md b/INSTALL.md index dcb57aaa..13e5c0ee 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -23,17 +23,17 @@ There are three options for installing **STLSoft**, depending on how you obtain If you obtain the latest release as an archive from the [**STLSoft** GitHub Releases](https://github.com/synesissoftware/STLSoft/releases) page (legacy archives may also still appear on [SourceForge](https://sourceforge.net/projects/stlsoft/)): -1. Download the latest distribution, e.g. [**STLSoft-1.11.1-rc7.zip**](https://github.com/synesissoftware/STLSoft/archive/refs/tags/1.11.1-rc7.zip); +1. Download the latest distribution, e.g. [**STLSoft-1.11.1-rc8.zip**](https://github.com/synesissoftware/STLSoft/archive/refs/tags/1.11.1-rc8.zip); 2. Unzip it to a directory of your choice, as in: ```bash $ mkdir -p ~/open-source $ cd ~/open-source -$ unzip STLSoft-1.11.1-rc7.zip +$ unzip STLSoft-1.11.1-rc8.zip ``` -3. Define an environment variable `STLSOFT`, whose value is the directory in which you unzipped it, e.g. `STLSOFT=~/open-source/STLSoft-1.11.1-rc7`, and then specify `$(STLSOFT)/include` (**UNIX**) or `%STLSOFT%\include` (**Windows**) in your project files and makefiles; +3. Define an environment variable `STLSOFT`, whose value is the directory in which you unzipped it, e.g. `STLSOFT=~/open-source/STLSoft-1.11.1-rc8`, and then specify `$(STLSOFT)/include` (**UNIX**) or `%STLSOFT%\include` (**Windows**) in your project files and makefiles; ### Cloning project, using environment variables @@ -100,16 +100,16 @@ $ sudo cmake --install ${SIS_CMAKE_BUILD_DIR:-./_build} --config Release Unless you are certain that your projects, and all the projects upon which they depend, all use **STLSoft** via **CMake** then it is recommended that you define an `STLSOFT` environment variable. For example, if you're using **UNIX** and you install to -**/usr/local/stlsoft/STLSoft-1.11.1-rc7** +**/usr/local/stlsoft/STLSoft-1.11.1-rc8** then you should set the environment variable `STLSOFT` to -**/usr/local/stlsoft/STLSoft-1.11.1-rc7** -in which case the file `stlsoft/stlsoft.h`, for example, will be located in `/usr/local/stlsoft/STLSoft-1.11.1-rc7/include/stlsoft/stlsoft.h`. +**/usr/local/stlsoft/STLSoft-1.11.1-rc8** +in which case the file `stlsoft/stlsoft.h`, for example, will be located in `/usr/local/stlsoft/STLSoft-1.11.1-rc8/include/stlsoft/stlsoft.h`. Conversely, if you're using **Windows** and you install to -**C:\3pty\STLSoft\STLSoft-1.11.1-rc7** +**C:\3pty\STLSoft\STLSoft-1.11.1-rc8** then you should set the environment variable `STLSOFT` to -**C:\3pty\STLSoft\STLSoft-1.11.1-rc7** -in which case the file `stlsoft/stlsoft.h`, for example, will be located in `C:\3pty\STLSoft\STLSoft-1.11.1-rc7\include\stlsoft\stlsoft.h`. +**C:\3pty\STLSoft\STLSoft-1.11.1-rc8** +in which case the file `stlsoft/stlsoft.h`, for example, will be located in `C:\3pty\STLSoft\STLSoft-1.11.1-rc8\include\stlsoft\stlsoft.h`. The makefiles / project files of several other open-source projects expect this symbol, including: @@ -143,7 +143,10 @@ This project comes with a number of scripts useful for building with **CMake**, | **build_cmake.sh** | Executes **CMake**-generated artefacts to (re)build the project.

Meaningful only once `prepare_cmake.sh` has been run to generate the **CMake** build artefacts.

Use `./build_cmake.sh --help` for further information. | | **clean_cmake.sh** | Executes **CMake**-generated artefacts to clean the project.

Meaningful only once `prepare_cmake.sh` has been run and a build has been run.

Use `./clean_cmake.sh --help` for further information. | | **run_all_examples.sh** | Runs all (matching) example programs.

Performs a build and then recursively runs executables under the examples tree. See also [**EXAMPLES.md**](./EXAMPLES.md).

Use `./run_all_examples.sh --help` for further information. | -| **run_all_unit_tests.sh** | Runs all (matching) unit-test programs.

Performs a build and then recursively runs executable programs matching the shell patterns `test_*` and `test.*`.

Use `./run_all_unit_tests.sh --help` for further information. | +| **run_all_unit_tests.sh** | Runs all (matching) unit-test programs.

Performs a build and then recursively runs executable programs matching the shell patterns `test_unit*` and `test.unit.*`.

Use `./run_all_unit_tests.sh --help` for further information. | +| **run_all_component_tests.sh** | Runs all (matching) component-test programs.

Performs a build and then recursively runs executable programs matching the shell patterns `test_component*` and `test.component.*`.

Use `./run_all_component_tests.sh --help` for further information. | +| **run_all_automated_tests.sh** | Runs all (matching) automated test programs: unit tests followed by component tests.

Use `./run_all_automated_tests.sh --help` for further information. | +| **generate_doxygen.sh** | Generates HTML API documentation from the public headers via **Doxygen**.

Writes to `./_build/doxygen/html/` (see **SIS_CMAKE_BUILD_DIR**); requires `doxygen` on the `PATH`.

Use `./generate_doxygen.sh --help` for further information. | | **remove_cmake_artefacts.sh** | Removes all known **CMake** artefacts.

Removes known **CMake** build artefacts from the build directory (currently `./_build`) to prepare for complete regeneration using `prepare_cmake.sh`.

Use `./remove_cmake_artefacts.sh --help` for further information. | diff --git a/NEWS.md b/NEWS.md index ade40c74..74839ad5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,7 @@ | Date | News Item | Details | | ------------------- | ----------------------------------- | ------- | +| 10th October 2026 | Release of [STLSoft 1.11.1-rc8](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc8) | environment_variable_strtoll()`; `unixstl::filesystem_traits<>::rewind_dir()`; `class_type` and `getenv_s()` empty-variable fixes; component tests; `cmake --build` helper scripts; **run_all_component_tests.sh** / **run_all_automated_tests.sh**; **generate_doxygen.sh**; Linguist classification | | 30th September 2026 | Release of [STLSoft 1.11.1-rc7](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc7) | counted NULs; `wchar_t` `string_begins_with`; bit intrinsics; `write_binary()`; `file_lines` mmap (~10–20× vs `fgetc`); compare and allocator units; MinGW static runtime; short test directories; nested **test/performance** | | 21st September 2026 | Release of [STLSoft 1.11.1-rc6](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc6) | ACE/ATL/MFC CI; Clang/macOS `stricmp` aliases | | 4th August 2026 | Release of [STLSoft 1.11.1-rc5](https://github.com/synesissoftware/STLSoft/releases/tag/1.11.1-rc5) | `basic_simple_string<>#resize()`; **CHANGES.md**; docs | diff --git a/build_cmake.sh b/build_cmake.sh index 55e6c4ac..a675a9da 100755 --- a/build_cmake.sh +++ b/build_cmake.sh @@ -1,22 +1,119 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} IgnoreRemainingFlagsAndOptions=0 +SisUseColours=0 Targets=() # ########################################################## -# functions +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { -function join_by { local IFS="$1"; shift; echo "$*"; } + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} # ########################################################## @@ -26,21 +123,16 @@ while [[ $# -gt 0 ]]; do if [ $IgnoreRemainingFlagsAndOptions -ne 0 ]; then - Targets+=($1) - + Targets+=("$1") shift - continue - else - - if [ ! ${1:0:1} = '-' ]; then - - Targets+=($1) + fi - shift + if [ "${1:0:1}" != '-' ]; then - continue - fi + Targets+=("$1") + shift + continue fi case $1 in @@ -48,18 +140,33 @@ while [[ $# -gt 0 ]]; do IgnoreRemainingFlagsAndOptions=1 ;; + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF Executes CMake-generated artefacts to (re)build project -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + (no arguments) + builds all default targets + + ... + builds one or more specific targets + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + standard flags: @@ -72,7 +179,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -85,41 +192,26 @@ done # ########################################################## # main() -if [ ! -d "$CMakeDir" ]; then +if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" + >&2 echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found or not configured; use script 'prepare_cmake.sh' first" exit 1 -else - - cd $CMakeDir - - if [ ! -f "$CMakeDir/Makefile" ]; then - - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' does not contain expected file 'Makefile', so a build cannot be performed. It is recommended that you remove all CMake artefacts using script 'remove_cmake_artefacts.sh' followed by regeneration via 'prepare_cmake.sh'" - - cd ->/dev/null - - exit 1 - else - - if [ -z "$Targets" ]; then - - echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`)" - else - - echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`) with specific target(s) $(join_by , "${Targets[@]}")" - fi +fi - $MakeCmd ${Targets[*]} - status=$? +if [ ${#Targets[@]} -eq 0 ]; then - cd ->/dev/null + echo "Executing build of ${ProjectNameClr} (via cmake --build)" + sis_cmake_build + status=$? +else - exit $status - fi + echo "Executing build of ${ProjectNameClr} (via cmake --build) with specific target(s): ${Targets[*]}" + sis_cmake_build "${Targets[@]}" + status=$? fi +exit $status -# ############################## end of file ############################# # +# ############################## end of file ############################# # diff --git a/clean_cmake.sh b/clean_cmake.sh index ead9c205..733afbdd 100755 --- a/clean_cmake.sh +++ b/clean_cmake.sh @@ -1,13 +1,117 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} # ########################################################## @@ -16,18 +120,27 @@ ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF -Executes CMake-generated artefacts to clean project +Cleans CMake-generated build artefacts via cmake --build --target clean -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + standard flags: @@ -40,7 +153,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -53,35 +166,16 @@ done # ########################################################## # main() -if [ ! -d "$CMakeDir" ]; then +if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" + >&2 echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found or not configured; nothing to clean" exit 1 -else - - cd $CMakeDir - - if [ ! -f "$CMakeDir/Makefile" ]; then - - >&2 echo "$ScriptPath: CMake build directory '$CMakeDir' does not contain expected file 'Makefile', so a clean cannot be performed. It is recommended that you remove all CMake artefacts using script 'remove_cmake_artefacts.sh' followed by regeneration via 'prepare_cmake.sh'" - - cd ->/dev/null - - exit 1 - else - - echo "Cleaning ${ProjectName} build (via command \`$MakeCmd clean\`)" - - $MakeCmd clean - status=$? - - cd ->/dev/null - - exit $status - fi fi +echo "Executing clean of ${ProjectNameClr} (via cmake --build --target clean)" +sis_cmake_build clean +exit $? -# ############################## end of file ############################# # +# ############################## end of file ############################# # diff --git a/ctest_cmake.sh b/ctest_cmake.sh index 24c21723..4e09ee21 100755 --- a/ctest_cmake.sh +++ b/ctest_cmake.sh @@ -1,16 +1,119 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 -CMakeVerbose= +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +CTestVerbose= RunMake=1 +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} # ########################################################## @@ -19,13 +122,17 @@ RunMake=1 while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --no-make|-M) RunMake=0 ;; --verbose|-V) - CMakeVerbose=--verbose + CTestVerbose=--verbose ;; --help) @@ -33,15 +140,20 @@ while [[ $# -gt 0 ]]; do cat << EOF Runs CMake's CTest test program(s) -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + -M --no-make - does not execute CMake and make before running tests + does not execute a build before running tests -V --verbose @@ -59,7 +171,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -76,29 +188,48 @@ status=0 if [ $RunMake -ne 0 ]; then - echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`) and then running all component and unit test programs" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running CTest" - mkdir -p $CMakeDir || exit 1 + mkdir -p "$CMakeDir" || exit 1 - cd $CMakeDir + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - $MakeCmd - status=$? + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" + + exit 1 + fi - cd ->/dev/null + sis_cmake_build + status=$? else - if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" - >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step" + exit 1 fi fi if [ $status -eq 0 ]; then - echo "Running CMake tests" + echo + echo "Running ${ProjectNameClr} CMake tests" + + # Multi-config generators (e.g. Visual Studio) need -C ; + # mirror sis_cmake_build's CMAKE_CONFIGURATION_TYPES detection. + ctest_args=(--test-dir "$CMakeDir" --output-on-failure) + if [ -n "$CTestVerbose" ]; then + + ctest_args+=("$CTestVerbose") + fi + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + ctest_args+=(-C "${SIS_CMAKE_CONFIG:-Release}") + fi - ctest --test-dir $CMakeDir $CMakeVerbose + ctest "${ctest_args[@]}" status=$? fi diff --git a/dev/bin/batch_compile_check b/dev/bin/batch_compile_check index b5274772..2bd4c0a6 100755 --- a/dev/bin/batch_compile_check +++ b/dev/bin/batch_compile_check @@ -14,7 +14,7 @@ ScriptPath=$0 -ProjectDir=$(cd $(dirname "$ScriptPath"); pwd) +ProjectDir=$(cd "$(dirname "$ScriptPath")" && pwd) Basename=$(basename "$ScriptPath") IncludeDir="$ProjectDir/../../include" ScratchDir="$ProjectDir/../../scratch" diff --git a/doc/mainpage.md b/doc/mainpage.md new file mode 100644 index 00000000..1ed8959d --- /dev/null +++ b/doc/mainpage.md @@ -0,0 +1,71 @@ +# STLSoft {#mainpage} + +**STLSoft** is a suite of **C** and **C++** libraries that provides STL extensions, general-purpose utility components, and facades over operating-system and technology-specific APIs. It is robust, lightweight, and cross-platform. + +The overarching characteristic of **STLSoft** is that it is *lightweight*. The libraries are **100% header-only**: all components are defined entirely within header files, so users need only `#include` the requisite files to access the functionality. **STLSoft** is *not* a framework; each component is as thin as possible for its given function, intended as a building block for higher-level software. + + +## Components + +The components are organised as *sub-projects* (technology / platform facets), each rooted at its own header directory: + +| Sub-project | Header | Summary | +| ----------- | ------ | ------- | +| **ACESTL** | `` | Components for the **ACE** framework | +| **ATLSTL** | `` | Components for the **Active Template Library (ATL)** | +| **COMSTL** | `` | Components for the **Component Object Model (COM)** | +| **InetSTL** | `` | Components for internet APIs | +| **MFCSTL** | `` | Components for the **Microsoft Foundation Classes (MFC)** | +| **PlatformSTL** | `` | Platform-selected facades over **UnixSTL** or **WinSTL** | +| **STLSoft** | `` | General-purpose components and base-level features | +| **UnixSTL** | `` | Components for **Unix** operating-system APIs | +| **WinSTL** | `` | Components for **Windows** operating-system APIs | + +Many functional areas (for example **filesystem**, **dl**, and **memory**) appear in more than one sub-project with *intersecting conformance*: similar components are structurally compatible, without being related by type, only to the degree of the intersection of their functionality. + + +## Quick start + +Include the requisite header(s) and use the component; there is nothing to link: + +```cpp +#include + +#include +#include + +int main() +{ + // up to 128 chars on the stack; beyond that, on the heap + stlsoft::auto_buffer buff(10); + + ::memset(&buff[0], '1', buff.size()); + + buff.resize(1000, '2'); + + return EXIT_SUCCESS; +} +``` + +Consumer **CMake** projects may use `find_package(STLSoft)` and link +`STLSoft::STLSoft`. Otherwise, define an environment variable `STLSOFT` and +add `$(STLSOFT)/include` to the include path. See +[INSTALL.md](https://github.com/synesissoftware/STLSoft/blob/master/INSTALL.md) +for build and install instructions, and +[EXAMPLES.md](https://github.com/synesissoftware/STLSoft/blob/master/EXAMPLES.md) +for the catalogue of example programs. + + +## Related projects + +Projects that depend on **STLSoft**: + +| Project | Use | +| ------- | --- | +| [b64](https://github.com/synesissoftware/b64) | C++ API is implemented in terms of **STLSoft** | +| [Pantheios](https://github.com/synesissoftware/Pantheios) | Logging API library | +| [shwild](https://github.com/synesissoftware/shwild) | Optional dependency (`USE_STLSOFT_PKG`) | +| [xTests](https://github.com/synesissoftware/xTests) | Testing library; used for the **STLSoft** unit and component tests | + + + diff --git a/examples/by_project/unixstl/filesystem/CMakeLists.txt b/examples/by_project/unixstl/filesystem/CMakeLists.txt index 7dd9ccf9..06433e78 100644 --- a/examples/by_project/unixstl/filesystem/CMakeLists.txt +++ b/examples/by_project/unixstl/filesystem/CMakeLists.txt @@ -1,3 +1,4 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten add_subdirectory(glob_sequence) add_subdirectory(readdir_sequence) +add_subdirectory(rewind_dir) diff --git a/examples/by_project/unixstl/filesystem/rewind_dir/CMakeLists.txt b/examples/by_project/unixstl/filesystem/rewind_dir/CMakeLists.txt new file mode 100644 index 00000000..69e650c3 --- /dev/null +++ b/examples/by_project/unixstl/filesystem/rewind_dir/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_example_program(example.unixstl.filesystem.rewind_dir main.cpp) diff --git a/examples/by_project/unixstl/filesystem/rewind_dir/main.cpp b/examples/by_project/unixstl/filesystem/rewind_dir/main.cpp new file mode 100644 index 00000000..23fec8ac --- /dev/null +++ b/examples/by_project/unixstl/filesystem/rewind_dir/main.cpp @@ -0,0 +1,127 @@ +/* ///////////////////////////////////////////////////////////////////////// + * Purpose: Illustrates the use of `unixstl::filesystem_traits<>` functions + * `open_dir()`, `read_dir()`, `rewind_dir()`, and `close_dir()` to + * enumerate a directory in two passes: the first to count the + * entries, and the second - after rewinding - to collect their + * names into a container sized in advance. + * + * ////////////////////////////////////////////////////////////////////// */ + + +#include + +#include + +#include +#include +#include + +#include +#include + + +typedef unixstl::filesystem_traits fs_traits_t; + + +int main(int argc, char* argv[]) +{ + char const* const program_name = platformstl::get_executable_name_from_path(argv[0]).ptr; + char const* root_dir = ss_nullptr_k; + + if (argc > 1 && 0 == strcmp("--help", argv[1])) + { + std::cout + << "USAGE: " + << program_name + << " []" + << std::endl; + + return EXIT_SUCCESS; + } + + switch (argc) + { + case 1: + + root_dir = "."; + break; + case 2: + + root_dir = argv[1]; + break; + + default: + + std::cerr + << program_name + << ": " + << "too many arguments; use --help for usage" + << std::endl; + + return EXIT_FAILURE; + } + + DIR* const h = fs_traits_t::open_dir(root_dir); + + if (NULL == h) + { + std::cerr + << program_name + << ": " + << "could not open directory '" + << root_dir + << "'" + << std::endl; + + return EXIT_FAILURE; + } + else + { + std::vector names; + size_t count = 0; + + // pass 1: count the entries + + while (NULL != fs_traits_t::read_dir(h)) + { + ++count; + } + + std::cout + << "'" + << root_dir + << "' contains " + << count + << " entries (including '.' and '..')" + << std::endl; + + // rewind, so that the next read_dir() returns the first entry again + + fs_traits_t::rewind_dir(h); + + // pass 2: collect the names, without any reallocation + + names.reserve(count); + + for (struct dirent const* de; NULL != (de = fs_traits_t::read_dir(h)); ) + { + names.push_back(de->d_name); + } + + fs_traits_t::close_dir(h); + + for (std::vector::const_iterator i = names.begin(); names.end() != i; ++i) + { + std::cout + << "\t" + << *i + << std::endl; + } + } + + return EXIT_SUCCESS; +} + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/execute_performance_tests.sh b/execute_performance_tests.sh index e15fbc44..b26b638c 100755 --- a/execute_performance_tests.sh +++ b/execute_performance_tests.sh @@ -1,7 +1,7 @@ #! /bin/bash ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) +Dir=$(cd "$(dirname "$ScriptPath")" && pwd) Basename=$(basename "$ScriptPath") CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} [[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make diff --git a/generate_doxygen.sh b/generate_doxygen.sh new file mode 100755 index 00000000..504b6619 --- /dev/null +++ b/generate_doxygen.sh @@ -0,0 +1,83 @@ +#! /bin/bash + +############################################################################# +# File: generate_doxygen.sh +# +# Purpose: Generates HTML API documentation from public headers via Doxygen +# +############################################################################# + +ScriptPath=$0 +Dir=$(cd "$(dirname "$ScriptPath")" && pwd) +Basename=$(basename "$ScriptPath") +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Generates HTML API documentation from public headers via Doxygen + +$ScriptPath [ ... flags/options ... ] + +Flags/options: + + standard flags: + + --help + displays this help and terminates + +Environment: + + SIS_CMAKE_BUILD_DIR + CMake build directory (default: /_build); documentation is + written to /doxygen/html/ + +EOF + + exit 0 + ;; + *) + + >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + + exit 1 + ;; + esac + + shift +done + + +# ########################################################## +# main() + +cd "$Dir" || exit 1 + +if ! command -v doxygen >/dev/null 2>&1; then + >&2 echo "$ScriptPath: doxygen not found on PATH" + exit 1 +fi + +mkdir -p "${CMakeDir}/doxygen" + +{ + cat Doxyfile + echo "" + echo "# Output directory (overridden by ${Basename})" + echo "OUTPUT_DIRECTORY = ${CMakeDir}/doxygen" +} | doxygen - + +echo "${ProjectName} API documentation written to ${CMakeDir}/doxygen/html/index.html" + + +# ############################## end of file ############################# # diff --git a/include/inetstl/filesystem/filesystem_traits.hpp b/include/inetstl/filesystem/filesystem_traits.hpp index 74fe9d1c..e8a2ffa6 100644 --- a/include/inetstl/filesystem/filesystem_traits.hpp +++ b/include/inetstl/filesystem/filesystem_traits.hpp @@ -5,11 +5,11 @@ * Unicode specialisations thereof. * * Created: 30th April 1999 - * Updated: 20th March 2025 + * Updated: 8th October 2026 * * Home: http://stlsoft.org/ * - * Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems + * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems * Copyright (c) 1999-2019, Matthew Wilson and Synesis Software * All rights reserved. * @@ -53,10 +53,10 @@ #define INETSTL_INCL_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION -# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 5 -# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 0 -# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_REVISION 2 -# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 98 +# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 5 +# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 0 +# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_REVISION 2 +# define INETSTL_VER_INETSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 99 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -135,23 +135,23 @@ struct filesystem_traits /// @{ public: /// The character type - typedef C char_type; + typedef C char_type; /// The size type - typedef is_size_t size_type; + typedef is_size_t size_type; /// The difference type - typedef is_ptrdiff_t difference_type; + typedef is_ptrdiff_t difference_type; /// The find data type - typedef WIN32_FIND_DATA find_data_type; + typedef WIN32_FIND_DATA find_data_type; /// The stat data type - typedef WIN32_FIND_DATA stat_data_type; + typedef WIN32_FIND_DATA stat_data_type; /// The current instantion of the type - typedef filesystem_traits class_type; + typedef filesystem_traits class_type; /// The (signed) integer type - typedef is_int_t int_type; + typedef is_int_t int_type; /// The Boolean type - typedef is_bool_t bool_type; + typedef is_bool_t bool_type; /// The type of system error codes - typedef DWORD error_type; + typedef DWORD error_type; /// @} /// \name File-system entry names @@ -274,15 +274,15 @@ struct filesystem_traits : public STLSOFT_NS_QUAL(c_string_traits) { public: - typedef is_char_a_t char_type; - typedef is_size_t size_type; - typedef is_ptrdiff_t difference_type; - typedef WIN32_FIND_DATAA find_data_type; - typedef WIN32_FIND_DATAA stat_data_type; - typedef filesystem_traits class_type; - typedef is_int_t int_type; - typedef is_bool_t bool_type; - typedef DWORD error_type; + typedef is_char_a_t char_type; + typedef is_size_t size_type; + typedef is_ptrdiff_t difference_type; + typedef WIN32_FIND_DATAA find_data_type; + typedef WIN32_FIND_DATAA stat_data_type; + typedef filesystem_traits class_type; + typedef is_int_t int_type; + typedef is_bool_t bool_type; + typedef DWORD error_type; public: // File-system entry names @@ -553,15 +553,15 @@ struct filesystem_traits : public STLSOFT_NS_QUAL(c_string_traits) { public: - typedef is_char_w_t char_type; - typedef is_size_t size_type; - typedef is_ptrdiff_t difference_type; - typedef WIN32_FIND_DATAW find_data_type; - typedef WIN32_FIND_DATAW stat_data_type; - typedef filesystem_traits class_type; - typedef is_int_t int_type; - typedef is_bool_t bool_type; - typedef DWORD error_type; + typedef is_char_w_t char_type; + typedef is_size_t size_type; + typedef is_ptrdiff_t difference_type; + typedef WIN32_FIND_DATAW find_data_type; + typedef WIN32_FIND_DATAW stat_data_type; + typedef filesystem_traits class_type; + typedef is_int_t int_type; + typedef is_bool_t bool_type; + typedef DWORD error_type; public: diff --git a/include/inetstl/inetstl.h b/include/inetstl/inetstl.h index db1fe52e..aca3309d 100644 --- a/include/inetstl/inetstl.h +++ b/include/inetstl/inetstl.h @@ -5,7 +5,7 @@ * platform discriminations, and definitions of types. * * Created: 24th April 2004 - * Updated: 17th September 2026 + * Updated: 8th October 2026 * * Home: http://stlsoft.org/ * @@ -50,8 +50,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define INETSTL_VER_INETSTL_H_INETSTL_MAJOR 3 # define INETSTL_VER_INETSTL_H_INETSTL_MINOR 11 -# define INETSTL_VER_INETSTL_H_INETSTL_REVISION 3 -# define INETSTL_VER_INETSTL_H_INETSTL_EDIT 578 +# define INETSTL_VER_INETSTL_H_INETSTL_REVISION 4 +# define INETSTL_VER_INETSTL_H_INETSTL_EDIT 579 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /** \file inetstl/inetstl.h @@ -145,12 +145,13 @@ # define _INETSTL_VER_1_4_2 0x010402ff /*!< Version 1.4.2 (with STLSoft 1.11.1 alpha 8) */ # define _INETSTL_VER_1_5_0_A01 0x01050041 /*!< Version 1.5.0 alpha 1 (with STLSoft 1.11.1 beta 2) */ # define _INETSTL_VER_1_5_0_A02 0x01050042 /*!< Version 1.5.0 alpha 2 (with STLSoft 1.11.1 rc 2) */ +# define _INETSTL_VER_1_5_0_A03 0x01050043 /*!< Version 1.5.0 alpha 3 (with STLSoft 1.11.1 rc 8) */ #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ #define _INETSTL_VER_MAJOR 1 #define _INETSTL_VER_MINOR 5 #define _INETSTL_VER_PATCH 0 -#define _INETSTL_VER_ALPHABETA 0x42 +#define _INETSTL_VER_ALPHABETA 0x43 #define _INETSTL_VER \ (0\ @@ -194,8 +195,8 @@ * STLSoft version compatibility check(s) */ -#if _STLSOFT_VER < 0x010b0182 -# error This version of the InetSTL libraries requires STLSoft version 1.11.1 beta 2, or later +#if _STLSOFT_VER < 0x010B01C8 +# error This version of the InetSTL libraries requires STLSoft version 1.11.1 rc 8, or later #endif /* _STLSOFT_VER */ diff --git a/include/platformstl/platformstl.h b/include/platformstl/platformstl.h index 3227cca8..d2862a2d 100644 --- a/include/platformstl/platformstl.h +++ b/include/platformstl/platformstl.h @@ -5,7 +5,7 @@ * platform discriminations, and definitions of types. * * Created: 20th March 2005 - * Updated: 17th September 2026 + * Updated: 10th October 2026 * * Home: http://stlsoft.org/ * @@ -49,8 +49,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_MAJOR 1 # define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_MINOR 15 -# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_REVISION 8 -# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_EDIT 63 +# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_REVISION 9 +# define PLATFORMSTL_VER_PLATFORMSTL_H_PLATFORMSTL_EDIT 64 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /** \file platformstl/platformstl.h @@ -145,13 +145,14 @@ # define _PLATFORMSTL_VER_1_9_2 0x010902ff /*!< Version 1.9.2 (with STLSoft 1.10.5) */ # define _PLATFORMSTL_VER_1_9_3 0x010903ff /*!< Version 1.9.3 (with STLSoft 1.11.1 alpha 8) */ # define _PLATFORMSTL_VER_1_10_0_A01 0x010a0041 /*!< Version 1.10.0 alpha 1 (with STLSoft 1.11.1 alpha 15) */ -# define _PLATFORMSTL_VER_1_10_0_B01 0x010a0081 /*!< Version 1.10.0 alpha 2 (with STLSoft 1.11.1 rc 3) */ +# define _PLATFORMSTL_VER_1_10_0_B01 0x010A0081 /*!< Version 1.10.0 beta 1 (with STLSoft 1.11.1 rc 3) */ +# define _PLATFORMSTL_VER_1_10_0_B02 0x010A0082 /*!< Version 1.10.0 beta 2 (with STLSoft 1.11.1 rc 8) */ #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ #define _PLATFORMSTL_VER_MAJOR 1 #define _PLATFORMSTL_VER_MINOR 10 #define _PLATFORMSTL_VER_PATCH 0 -#define _PLATFORMSTL_VER_ALPHABETA 0x81 +#define _PLATFORMSTL_VER_ALPHABETA 0x82 #define _PLATFORMSTL_VER \ (0\ diff --git a/include/platformstl/process/functions.h b/include/platformstl/process/functions.h new file mode 100644 index 00000000..d7af5bd0 --- /dev/null +++ b/include/platformstl/process/functions.h @@ -0,0 +1,155 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: platformstl/process/functions.h + * + * Purpose: Process functions. + * + * Created: 8th October 2026 + * Updated: 8th October 2026 + * + * Home: http://stlsoft.org/ + * + * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name(s) of Matthew Wilson and Synesis Information Systems + * nor the names of any contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ////////////////////////////////////////////////////////////////////// */ + + +/** \file platformstl/process/functions.h + * + * \brief [C, C++] Process functions + * (\ref group__library__System "System" Library). + */ + +#ifndef PLATFORMSTL_INCL_PLATFORMSTL_PROCESS_H_FUNCTIONS +#define PLATFORMSTL_INCL_PLATFORMSTL_PROCESS_H_FUNCTIONS + +#ifndef PLATFORMSTL_DOCUMENTATION_SKIP_SECTION +# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_MAJOR 1 +# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_MINOR 0 +# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_REVISION 1 +# define PLATFORMSTL_VER_PLATFORMSTL_PROCESS_H_FUNCTIONS_EDIT 1 +#endif /* !PLATFORMSTL_DOCUMENTATION_SKIP_SECTION */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +#ifndef PLATFORMSTL_INCL_PLATFORMSTL_H_PLATFORMSTL +# include +#endif /* !PLATFORMSTL_INCL_PLATFORMSTL_H_PLATFORMSTL */ +#ifdef STLSOFT_TRACE_INCLUDE +# pragma message(__FILE__) +#endif /* STLSOFT_TRACE_INCLUDE */ + +#if 0 +#elif defined(PLATFORMSTL_OS_IS_UNIX) + +# include +#elif defined(PLATFORMSTL_OS_IS_WINDOWS) + +# include +#else + +# error Operating system not discriminated +#endif + +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * namespace + */ + +#if !defined(PLATFORMSTL_NO_NAMESPACE) && \ + !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) +# if defined(STLSOFT_NO_NAMESPACE) +/* There is no stlsoft namespace, so must define ::platformstl */ +namespace platformstl +{ +# else +/* Define stlsoft::platformstl_project */ +namespace stlsoft +{ +namespace platformstl_project +{ +# endif /* STLSOFT_NO_NAMESPACE */ +#endif /* !PLATFORMSTL_NO_NAMESPACE */ + + +/* ///////////////////////////////////////////////////////////////////////// + * functions (C) + */ + +STLSOFT_INLINE +int +platformstl_C_get_current_process_id(void) +{ +#if 0 +#elif defined(PLATFORMSTL_OS_IS_UNIX) + + return UNIXSTL_NS_QUAL(unixstl_C_get_current_process_id)(); +#elif defined(PLATFORMSTL_OS_IS_WINDOWS) + + return WINSTL_NS_QUAL(winstl_C_get_current_process_id)(); +#else + +# error Platform not discriminated +#endif +} + + +/* ///////////////////////////////////////////////////////////////////////// + * namespace + */ + +#ifndef PLATFORMSTL_NO_NAMESPACE +# if defined(STLSOFT_NO_NAMESPACE) || \ + defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) +} /* namespace platformstl */ +# else +} /* namespace platformstl_project */ +} /* namespace stlsoft */ +# endif /* STLSOFT_NO_NAMESPACE */ +#endif /* !PLATFORMSTL_NO_NAMESPACE */ + + +/* ///////////////////////////////////////////////////////////////////////// + * inclusion control + */ + +#ifdef STLSOFT_CF_PRAGMA_ONCE_SUPPORT +# pragma once +#endif /* STLSOFT_CF_PRAGMA_ONCE_SUPPORT */ + +#endif /* !PLATFORMSTL_INCL_PLATFORMSTL_PROCESS_H_FUNCTIONS */ + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/include/stlsoft/stlsoft.h b/include/stlsoft/stlsoft.h index 1293605b..51d82f0c 100644 --- a/include/stlsoft/stlsoft.h +++ b/include/stlsoft/stlsoft.h @@ -5,7 +5,7 @@ * and platform discriminations, and definitions of types. * * Created: 15th January 2002 - * Updated: 30th September 2026 + * Updated: 10th October 2026 * * Home: http://stlsoft.org/ * @@ -55,8 +55,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define STLSOFT_VER_STLSOFT_H_STLSOFT_MAJOR 3 # define STLSOFT_VER_STLSOFT_H_STLSOFT_MINOR 57 -# define STLSOFT_VER_STLSOFT_H_STLSOFT_REVISION 19 -# define STLSOFT_VER_STLSOFT_H_STLSOFT_EDIT 607 +# define STLSOFT_VER_STLSOFT_H_STLSOFT_REVISION 20 +# define STLSOFT_VER_STLSOFT_H_STLSOFT_EDIT 608 #else /* ? STLSOFT_DOCUMENTATION_SKIP_SECTION */ /* # include "./internal/doxygen_defs.h" */ #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -421,13 +421,14 @@ # define _STLSOFT_VER_1_11_1_RC4 0x010b01c4 /*!< Version 1.11.1 rc 4 (1st July 2026) */ # define _STLSOFT_VER_1_11_1_RC5 0x010b01c5 /*!< Version 1.11.1 rc 5 (4th August 2026) */ # define _STLSOFT_VER_1_11_1_RC6 0x010b01c6 /*!< Version 1.11.1 rc 6 (21st September 2026) */ -# define _STLSOFT_VER_1_11_1_RC7 0x010b01c7 /*!< Version 1.11.1 rc 7 (30th September 2026) */ +# define _STLSOFT_VER_1_11_1_RC7 0x010B01C7 /*!< Version 1.11.1 rc 7 (30th September 2026) */ +# define _STLSOFT_VER_1_11_1_RC8 0x010B01C8 /*!< Version 1.11.1 rc 8 (10th October 2026) */ #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ #define _STLSOFT_VER_MAJOR 1 #define _STLSOFT_VER_MINOR 11 #define _STLSOFT_VER_PATCH 1 -#define _STLSOFT_VER_ALPHABETA 0xc7 +#define _STLSOFT_VER_ALPHABETA 0xC8 #define _STLSOFT_VER \ (0\ diff --git a/include/stlsoft/system/environment/functions.h b/include/stlsoft/system/environment/functions.h index 5b81b824..ce665a09 100644 --- a/include/stlsoft/system/environment/functions.h +++ b/include/stlsoft/system/environment/functions.h @@ -4,7 +4,7 @@ * Purpose: Environment functions. * * Created: 4th March 2011 - * Updated: 28th September 2026 + * Updated: 8th October 2026 * * Home: http://stlsoft.org/ * @@ -52,9 +52,9 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_MAJOR 1 -# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_MINOR 0 -# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_REVISION 3 -# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_EDIT 15 +# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_MINOR 1 +# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_REVISION 0 +# define STLSOFT_VER_STLSOFT_SYSTEM_ENVIRONMENT_H_FUNCTIONS_EDIT 18 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -69,6 +69,10 @@ # pragma message(__FILE__) #endif /* STLSOFT_TRACE_INCLUDE */ +#ifndef STLSOFT_INCL_STLSOFT_MEMORY_H_AUTO_BUFFER +# include +#endif /* !STLSOFT_INCL_STLSOFT_MEMORY_H_AUTO_BUFFER */ + #ifndef STLSOFT_INCL_STLSOFT_INTERNAL_H_SAFESTR # include #endif /* !STLSOFT_INCL_STLSOFT_INTERNAL_H_SAFESTR */ @@ -81,6 +85,10 @@ # define STLSOFT_INCL_H_STDLIB # include #endif /* !STLSOFT_INCL_H_STDLIB */ +#ifndef STLSOFT_INCL_H_STRING +# define STLSOFT_INCL_H_STRING +# include +#endif /* !STLSOFT_INCL_H_STRING */ /* ///////////////////////////////////////////////////////////////////////// @@ -98,11 +106,13 @@ namespace stlsoft */ /** Indicates whether the named environment variable exists. + * + * \param name The name of the environment variable. May not be NULL; * * \retval 0 The environment variable does not exist; * \retval !0 The environment variable does exist; * - * \pre (NULL != name) + * \pre NULL != name; */ STLSOFT_INLINE ss_truthy_t @@ -119,7 +129,7 @@ stlsoft_C_environment_variable_exists_m( if (0 == e) { - if ('\0' == buff[0]) + if (0 == n) { // variable doesn't exist return 0; @@ -162,6 +172,155 @@ stlsoft_C_environment_variable_exists_a( #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ +/** Attempts to read a signed 64-bit integer value from the named + * environment variable. + * + * \param name The name of the environment variable. May not be NULL; + * \param result Pointer to a variable to receive the numeric result. On + * failure it is set to 0; + * + * \retval 0 The variable does not exist, the value is not a complete + * signed 64-bit integer, or the value is out of range. errno is EINVAL or + * ERANGE; + * \retval !0 The value was parsed and written through \c result; + * + * \pre NULL != name; + * \pre NULL != result; + * + * \note Interpreted with base 0, as with strtoll(): optional leading + * whitespace, an optional sign, then decimal, octal (leading 0), or + * hexadecimal (leading 0x). Unlike strtoll(), which accepts a numeric + * prefix followed by other characters, the entire value must be consumed. + * The complete environment-variable value is read before parsing. + */ +STLSOFT_INLINE +ss_truthy_t +stlsoft_C_environment_variable_strtoll_m( + char const* name +, ss_sint64_t* result +) STLSOFT_NOEXCEPT +{ + STLSOFT_ASSERT(NULL != name); + STLSOFT_ASSERT(NULL != result); + +#if 0 +#elif defined(STLSOFT_USING_SAFE_STR_FUNCTIONS) && \ + defined(STLSOFT_COMPILER_IS_MSVC) + + { + STLSOFT_C_AUTO_BUFFER_DECLARE(char, 21, buff); + size_t n; + errno_t e; + + STLSOFT_C_AUTO_BUFFER_INITIALISE_FROM_INTERNAL(buff); + + e = STLSOFT_NS_GLOBAL(getenv_s)( + &n + , buff.ptr + , buff.size + , name + ); + + if (ERANGE == e) + { + e = STLSOFT_C_AUTO_BUFFER_RESIZE(buff, n); + + if (0 == e) + { + e = STLSOFT_NS_GLOBAL(getenv_s)( + &n + , buff.ptr + , buff.size + , name + ); + } + } + + if (0 != e) + { + STLSOFT_C_AUTO_BUFFER_FREE(buff); + + errno = e; + + *result = 0; + + return 0; + } + else + { + char* endptr; + + errno = 0; + + *result = STLSOFT_NS_GLOBAL(strtoll)(buff.ptr, &endptr, 0); + + if (buff.ptr == endptr || + '\0' != *endptr || + ERANGE == errno) + { + if (0 == errno) + { + errno = EINVAL; + } + + *result = 0; + + STLSOFT_C_AUTO_BUFFER_FREE(buff); + + return 0; + } + else + { + STLSOFT_C_AUTO_BUFFER_FREE(buff); + + return 1; + } + } + } +#else + + { + char const* const value = STLSOFT_NS_GLOBAL(getenv)(name); + + if (NULL == value) + { + errno = EINVAL; + + *result = 0; + + return 0; + } + else + { + char* endptr; + + errno = 0; + + *result = STLSOFT_NS_GLOBAL(strtoll)(value, &endptr, 0); + + if (value == endptr || + '\0' != *endptr || + ERANGE == errno) + { + if (0 == errno) + { + errno = EINVAL; + } + + *result = 0; + + return 0; + } + else + { + return 1; + } + } + } +#endif +} + + /* ///////////////////////////////////////////////////////////////////////// * language */ @@ -176,12 +335,22 @@ stlsoft_C_environment_variable_exists_a( /** \see stlsoft_C_environment_variable_exists_m */ inline -ss_truthy_t +bool environment_variable_exists( char const* name -) +) STLSOFT_NOEXCEPT { - return stlsoft_C_environment_variable_exists_m(name); + return 0 != stlsoft_C_environment_variable_exists_m(name); +} + +inline +bool +environment_variable_strtoll( + char const* name +, ss_sint64_t* result +) STLSOFT_NOEXCEPT +{ + return 0 != stlsoft_C_environment_variable_strtoll_m(name, result); } diff --git a/include/unixstl/filesystem/filesystem_traits.hpp b/include/unixstl/filesystem/filesystem_traits.hpp index b8aab10e..c392cdd0 100644 --- a/include/unixstl/filesystem/filesystem_traits.hpp +++ b/include/unixstl/filesystem/filesystem_traits.hpp @@ -5,7 +5,7 @@ * Unicode specialisations thereof. * * Created: 15th November 2002 - * Updated: 6th May 2025 + * Updated: 8th October 2026 * * Thanks: To Sergey Nikulov, for spotting a preprocessor typo that broke * GCC -pedantic; to Michal Makowski and Zar Eindl for reporting @@ -14,7 +14,7 @@ * * Home: http://stlsoft.org/ * - * Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems + * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems * Copyright (c) 2002-2019, Matthew Wilson and Synesis Software * All rights reserved. * @@ -59,9 +59,9 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MAJOR 4 -# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 16 +# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_MINOR 17 # define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_REVISION 1 -# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 188 +# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS_EDIT 190 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -120,7 +120,7 @@ #endif /* !STLSOFT_INCL_H_FCNTL */ #ifdef _WIN32 # include -# if defined(STLSOFT_COMPILER_IS_INTEL) || \ +# if defined(STLSOFT_COMPILER_IS_INTEL) ||\ defined(STLSOFT_COMPILER_IS_MSVC) # include # endif /* os && compiler */ @@ -177,7 +177,7 @@ */ #ifndef UNIXSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) /* There is no stlsoft namespace, so must define ::unixstl */ namespace unixstl @@ -731,6 +731,11 @@ struct filesystem_traits static DIR* open_dir(char_type const* dir); /// Read an entry from the file-system search static struct dirent const* read_dir(DIR* h); + /// Resets the file-system search to the first entry, such that the + /// next call to read_dir() returns the first entry + /// + /// \pre NULL != h + static void rewind_dir(DIR* h); /// Closes the handle of the file-system search static void close_dir(DIR* h); /// @} @@ -791,7 +796,7 @@ struct filesystem_traits static bool_type is_device(char_type const* path); /// Returns whether the given path represents a socket static bool_type is_socket(char_type const* path); -#endif /* OS */ +#endif /* _WIN32 */ /// Returns whether the given path represents a link static bool_type is_link(char_type const* path); @@ -811,7 +816,7 @@ struct filesystem_traits static bool_type is_device(stat_data_type const* stat_data); /// Returns whether the given stat info represents a socket static bool_type is_socket(stat_data_type const* stat_data); -#endif /* OS */ +#endif /* _WIN32 */ /// Returns whether the given stat info represents a link static bool_type is_link(stat_data_type const* stat_data); @@ -900,7 +905,8 @@ struct filesystem_traits_ public: // path classification and analysis static size_type path_max() { -#if defined(PATH_MAX) +#if 0 +#elif defined(PATH_MAX) return PATH_MAX; #else /* ? PATH_MAX */ @@ -924,7 +930,7 @@ struct filesystem_traits typedef us_ptrdiff_t difference_type; typedef struct stat stat_data_type; typedef struct stat fstat_data_type; - typedef filesystem_traits class_type; + typedef filesystem_traits class_type; typedef path_classification_t path_classification_type; typedef unixstl_C_path_classification_results_m_t path_classification_results_type; typedef stlsoft_C_string_slice_m_t path_classification_string_slice_type; @@ -1356,7 +1362,6 @@ struct filesystem_traits static bool_type is_path_rooted(char_type const* path) { UNIXSTL_ASSERT(NULL != path); - #ifdef _WIN32 // It might be a UNC path. This is handled by the second test below, but @@ -1389,7 +1394,6 @@ struct filesystem_traits ) { UNIXSTL_ASSERT(NULL != path); - #ifdef _WIN32 if (len >= 2) @@ -1426,7 +1430,6 @@ struct filesystem_traits static bool_type is_path_absolute(char_type const* path) { UNIXSTL_ASSERT(NULL != path); - #ifdef _WIN32 // If it's really on Windows, then it can only be absolute if ... @@ -1460,7 +1463,6 @@ struct filesystem_traits { return false; } - #ifdef _WIN32 // If it's really on Windows, then it can only be absolute if ... @@ -1491,7 +1493,6 @@ struct filesystem_traits static bool_type is_path_UNC(char_type const* path) { UNIXSTL_ASSERT(NULL != path); - #ifdef _WIN32 size_type const len = str_len(path); @@ -1674,7 +1675,6 @@ struct filesystem_traits { return true; } - #ifdef _WIN32 if (is_root_drive_(path, cchPath)) @@ -1686,7 +1686,7 @@ struct filesystem_traits { return true; } -#endif +#endif /* _WIN32 */ return false; } @@ -1763,9 +1763,9 @@ struct filesystem_traits if (NULL == slash2) { -#if 0 +# if 0 WINSTL_API_EXTERNAL_ErrorHandling_SetLastError(ERROR_INVALID_ARGUMENT); -#endif /* 0 */ +# endif /* 0 */ return 0; } @@ -1798,7 +1798,7 @@ struct filesystem_traits get_full_path_name_impl2( char_type const* fileName , size_type const len - , char_type* buffer + , char_type buffer[] , size_type cchBuffer ) { @@ -1828,7 +1828,7 @@ struct filesystem_traits return get_full_path_name_impl2(cwd.data(), n2 + len, buffer, cchBuffer); } -#endif +#endif /* _WIN32 */ // The next thing to so is determine whether the path is absolute, in // which case we'll just copy it into the buffer @@ -1836,10 +1836,10 @@ struct filesystem_traits #ifdef _WIN32 if (is_path_absolute(fileName)) -#else +#else /* ? _WIN32 */ if (is_path_rooted(fileName)) -#endif +#endif /* _WIN32 */ { // Given path is absolute, so simply copy into buffer if (NULL == buffer) @@ -1952,9 +1952,12 @@ struct filesystem_traits } // For some reason OS-X's GCC needs this : it's confused. -#if defined(STLSOFT_COMPILER_IS_GCC) && \ - STLSOFT_GCC_VER >= 40200 && \ - STLSOFT_GCC_VER < 40300 +#if 0 +#elif 1 &&\ + defined(STLSOFT_COMPILER_IS_GCC) &&\ + STLSOFT_GCC_VER >= 40200 &&\ + STLSOFT_GCC_VER < 40300 &&\ + 1 UNIXSTL_ASSERT(0); // should never get here return 0; #endif @@ -2185,6 +2188,12 @@ return 0; { return ::readdir(h); } + static void rewind_dir(DIR* h) + { + UNIXSTL_ASSERT(NULL != h); + + ::rewinddir(h); + } static void close_dir(DIR* h) { ::closedir(h); @@ -2197,9 +2206,12 @@ return 0; char_type const* dir ) { -#if defined(_WIN32) && \ - ( defined(STLSOFT_COMPILER_IS_MSVC) || \ - defined(STLSOFT_COMPILER_IS_INTEL)) +#if 0 +#elif defined(_WIN32) &&\ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + 0) return 0 == ::_chdir(dir); #else /* ? _WIN32 */ @@ -2240,10 +2252,15 @@ return 0; private: static char_type const* call_getcwd_(char_type buffer[], size_type cchBuffer) { -#if defined(STLSOFT_COMPILER_IS_INTEL) || \ - defined(STLSOFT_COMPILER_IS_MSVC) +#if 0 +#elif 0 ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + 0 + return ::_getcwd(buffer, int(cchBuffer)); #else /* ? compiler */ + return ::getcwd(buffer, int(cchBuffer)); #endif /* compiler */ } @@ -2256,7 +2273,8 @@ return 0; , size_type cchBuffer ) { -#if defined(_WIN32) +#if 0 +#elif defined(_WIN32) char_type local[1 + _MAX_PATH]; size_type const cchLocal = STLSOFT_NUM_ELEMENTS(local); @@ -2373,7 +2391,7 @@ return 0; return class_type::stat(path, &sd) && class_type::is_socket(&sd); } -#endif /* OS */ +#endif /* _WIN32 */ static bool_type is_link(char_type const* path) { #ifdef _WIN32 @@ -2393,7 +2411,6 @@ return 0; { UNIXSTL_ASSERT(NULL != path); UNIXSTL_ASSERT(NULL != stat_data); - #ifdef _WIN32 if (NULL != class_type::str_pbrk(path, "*?")) @@ -2477,42 +2494,42 @@ return 0; static bool_type is_device(stat_data_type const* stat_data) { -#if 1 +# if 1 switch (stat_data->st_mode & S_IFMT) { -#ifdef S_IFBLK +# ifdef S_IFBLK case S_IFBLK: -#endif // S_IFBLK -#ifdef S_IFCHR +# endif // S_IFBLK +# ifdef S_IFCHR case S_IFCHR: -#endif // S_IFCHR -#ifdef S_IFIFO +# endif // S_IFCHR +# ifdef S_IFIFO case S_IFIFO: -#endif // S_IFIFO -#ifdef S_IFWHT +# endif // S_IFIFO +# ifdef S_IFWHT case S_IFWHT: -#endif // S_IFWHT +# endif // S_IFWHT return true; default: return false; } -#else /* ? 0 */ +# else /* ? 0 */ return false; -#endif /* 0 */ +# endif /* 0 */ } static bool_type is_socket(stat_data_type const* stat_data) { -#if 1 +# if 1 return S_IFSOCK == (stat_data->st_mode & S_IFMT); -#else /* ? 0 */ +# else /* ? 0 */ return filesystem_traits_util_::is_file_type_(stat_data, S_IFSOCK); -#endif /* 0 */ +# endif /* 0 */ } -#endif /* OS */ +#endif /* _WIN32 */ static bool_type is_link(stat_data_type const* stat_data) { #ifdef _WIN32 @@ -2558,10 +2575,13 @@ return 0; static bool_type create_directory(char_type const* dir, mode_type permissions) { -#if defined(_WIN32) && \ - ( defined(STLSOFT_COMPILER_IS_INTEL) || \ - defined(STLSOFT_COMPILER_IS_GCC) || \ - defined(STLSOFT_COMPILER_IS_MSVC)) +#if 0 +#elif defined(_WIN32) &&\ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + defined(STLSOFT_COMPILER_IS_GCC) ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + 0) STLSOFT_SUPPRESS_UNUSED(permissions); @@ -2580,9 +2600,12 @@ return 0; static bool_type remove_directory(char_type const* dir) { -#if defined(_WIN32) && \ - ( defined(STLSOFT_COMPILER_IS_INTEL) || \ - defined(STLSOFT_COMPILER_IS_MSVC)) +#if 0 +#elif defined(_WIN32) &&\ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + 0) return 0 == ::_rmdir(dir); #else /* ? _WIN32 */ @@ -2615,9 +2638,12 @@ return 0; static file_handle_type open_file(char_type const* fileName, int oflag, int pmode) { -#if defined(_WIN32) && \ - ( defined(STLSOFT_COMPILER_IS_INTEL) || \ - defined(STLSOFT_COMPILER_IS_MSVC)) +#if 0 +#elif defined(_WIN32) &&\ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + 0) return ::_open(fileName, oflag, pmode); #else /* ? _WIN32 */ @@ -2630,9 +2656,12 @@ return 0; static bool_type close_file(file_handle_type h) { -#if defined(_WIN32) && \ - ( defined(STLSOFT_COMPILER_IS_INTEL) || \ - defined(STLSOFT_COMPILER_IS_MSVC)) +#if 0 +#elif defined(_WIN32) &&\ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + 0) return 0 == ::_close(h); #else /* ? _WIN32 */ @@ -2685,7 +2714,7 @@ struct filesystem_traits typedef us_ptrdiff_t difference_type; typedef struct stat stat_data_type; typedef struct stat fstat_data_type; - typedef filesystem_traits class_type; + typedef filesystem_traits class_type; typedef us_int_t int_type; typedef us_bool_t bool_type; typedef int file_handle_type; @@ -3164,6 +3193,8 @@ struct filesystem_traits ; static struct dirent const* read_dir(DIR* h) ; + static void rewind_dir(DIR* h) + ; static void close_dir(DIR* h) ; @@ -3199,7 +3230,7 @@ struct filesystem_traits */ #ifndef UNIXSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) } // namespace unixstl # else diff --git a/include/unixstl/process/functions.h b/include/unixstl/process/functions.h new file mode 100644 index 00000000..ddf65e78 --- /dev/null +++ b/include/unixstl/process/functions.h @@ -0,0 +1,173 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: unixstl/process/functions.h + * + * Purpose: Process functions. + * + * Created: 8th October 2026 + * Updated: 8th October 2026 + * + * Home: http://stlsoft.org/ + * + * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name(s) of Matthew Wilson and Synesis Information Systems + * nor the names of any contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ////////////////////////////////////////////////////////////////////// */ + + +/** \file unixstl/process/functions.h + * + * \brief [C, C++] Process functions + * (\ref group__library__System "System" Library). + */ + +#ifndef UNIXSTL_INCL_UNIXSTL_PROCESS_H_FUNCTIONS +#define UNIXSTL_INCL_UNIXSTL_PROCESS_H_FUNCTIONS + +#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION +# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_MAJOR 1 +# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_MINOR 0 +# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_REVISION 1 +# define UNIXSTL_VER_UNIXSTL_PROCESS_H_FUNCTIONS_EDIT 1 +#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +#ifndef UNIXSTL_INCL_UNIXSTL_H_UNIXSTL +# include +#endif /* !UNIXSTL_INCL_UNIXSTL_H_UNIXSTL */ +#ifdef STLSOFT_TRACE_INCLUDE +# pragma message(__FILE__) +#endif /* STLSOFT_TRACE_INCLUDE */ + +#if 0 +#elif defined(_WIN32) &&\ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + 0) + +# ifndef STLSOFT_INCL_H_PROCESS +# define STLSOFT_INCL_H_PROCESS +# include +# endif /* !STLSOFT_INCL_H_PROCESS */ +#else + +# ifndef STLSOFT_INCL_SYS_H_TYPES +# define STLSOFT_INCL_SYS_H_TYPES +# include +# endif /* !STLSOFT_INCL_SYS_H_TYPES */ +# ifndef STLSOFT_INCL_H_UNISTD +# define STLSOFT_INCL_H_UNISTD +# include +# endif /* !STLSOFT_INCL_H_UNISTD */ +#endif + + +/* ///////////////////////////////////////////////////////////////////////// + * namespace + */ + +#if !defined(UNIXSTL_NO_NAMESPACE) && \ + !defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) +# if defined(STLSOFT_NO_NAMESPACE) +/* There is no stlsoft namespace, so must define ::unixstl */ +namespace unixstl +{ +# else +/* Define stlsoft::unixstl_project */ +namespace stlsoft +{ +namespace unixstl_project +{ +# endif /* STLSOFT_NO_NAMESPACE */ +#endif /* !UNIXSTL_NO_NAMESPACE */ + + +/* ///////////////////////////////////////////////////////////////////////// + * functions + */ + +/** \brief Obtains the id of the calling process. + * + * \ingroup group__library__System + * + * \return The process id, as obtained from \c _getpid() on Windows / UNIXem + * builds or \c getpid() otherwise. + */ +STLSOFT_INLINE +int +unixstl_C_get_current_process_id(void) +{ +#if 0 +#elif defined(_WIN32) && \ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + 0) + + return STLSOFT_NS_GLOBAL(_getpid)(); +#else + + STLSOFT_STATIC_ASSERT(sizeof(pid_t) <= sizeof(int)); + + return STLSOFT_STATIC_CAST(int, STLSOFT_NS_GLOBAL(getpid)()); +#endif +} + + +/* ///////////////////////////////////////////////////////////////////////// + * namespace + */ + +#ifndef UNIXSTL_NO_NAMESPACE +# if defined(STLSOFT_NO_NAMESPACE) || \ + defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) +} /* namespace unixstl */ +# else +} /* namespace unixstl_project */ +} /* namespace stlsoft */ +# endif /* STLSOFT_NO_NAMESPACE */ +#endif /* !UNIXSTL_NO_NAMESPACE */ + + +/* ///////////////////////////////////////////////////////////////////////// + * inclusion control + */ + +#ifdef STLSOFT_CF_PRAGMA_ONCE_SUPPORT +# pragma once +#endif /* STLSOFT_CF_PRAGMA_ONCE_SUPPORT */ + +#endif /* !UNIXSTL_INCL_UNIXSTL_PROCESS_H_FUNCTIONS */ + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/include/unixstl/system/system_traits.hpp b/include/unixstl/system/system_traits.hpp index 1dd668ff..3be24177 100644 --- a/include/unixstl/system/system_traits.hpp +++ b/include/unixstl/system/system_traits.hpp @@ -5,11 +5,11 @@ * Unicode specialisations thereof. * * Created: 15th November 2002 - * Updated: 20th March 2025 + * Updated: 8th October 2026 * * Home: http://stlsoft.org/ * - * Copyright (c) 2019-2025, Matthew Wilson and Synesis Information Systems + * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems * Copyright (c) 2002-2019, Matthew Wilson and Synesis Software * All rights reserved. * @@ -55,8 +55,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_MAJOR 6 # define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_MINOR 2 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_REVISION 2 -# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_EDIT 140 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_REVISION 3 +# define UNIXSTL_VER_UNIXSTL_SYSTEM_HPP_SYSTEM_TRAITS_EDIT 141 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -86,8 +86,9 @@ # include #endif /* !STLSOFT_INCL_STLSOFT_API_external_h_string */ -#if defined(_WIN32) || \ - defined(_WIN64) +#if 0 +#elif defined(_WIN32) ||\ + defined(_WIN64) # include #endif /* Windows */ #ifndef STLSOFT_INCL_H_ERRNO @@ -98,10 +99,11 @@ # define STLSOFT_INCL_H_FCNTL # include #endif /* !STLSOFT_INCL_H_FCNTL */ -#if defined(_WIN32) || \ - defined(_WIN64) +#if 0 +#elif defined(_WIN32) ||\ + defined(_WIN64) # include -# if defined(STLSOFT_COMPILER_IS_INTEL) || \ +# if defined(STLSOFT_COMPILER_IS_INTEL) ||\ defined(STLSOFT_COMPILER_IS_MSVC) # include # endif /* os && compiler */ @@ -149,7 +151,7 @@ */ #ifndef UNIXSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) /* There is no stlsoft namespace, so must define ::unixstl */ namespace unixstl @@ -408,9 +410,12 @@ struct system_traits_ public: static bool_type close_handle(handle_type h) { -#if defined(_WIN32) && \ - ( defined(STLSOFT_COMPILER_IS_MSVC) || \ - defined(STLSOFT_COMPILER_IS_INTEL)) +#if 0 +#elif defined(_WIN32) &&\ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + 0) return 0 == ::_close(h); #else /* ? _WIN32 */ @@ -462,7 +467,7 @@ struct system_traits typedef us_char_a_t char_type; typedef us_size_t size_type; typedef us_ptrdiff_t difference_type; - typedef system_traits class_type; + typedef system_traits class_type; typedef us_int_t int_type; typedef us_bool_t bool_type; typedef void* module_type; @@ -484,7 +489,6 @@ struct system_traits for (;; ++ps1, ++ps2) { int_type d = int_type(*ps1) - int_type(*ps2); - #ifdef _WIN32 if (0 != d) @@ -540,7 +544,6 @@ struct system_traits for (; 0 != cch; ++ps1, ++ps2, --cch) { int_type d = int_type(*ps1) - int_type(*ps2); - #ifdef _WIN32 if (0 != d) @@ -743,7 +746,7 @@ struct system_traits typedef us_char_w_t char_type; typedef us_size_t size_type; typedef us_ptrdiff_t difference_type; - typedef system_traits class_type; + typedef system_traits class_type; typedef us_int_t int_type; typedef us_bool_t bool_type; typedef void* module_type; @@ -869,7 +872,7 @@ struct system_traits /* ////////////////////////////////////////////////////////////////////// */ #ifndef UNIXSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) } // namespace unixstl # else diff --git a/include/unixstl/unixstl.h b/include/unixstl/unixstl.h index 390583b5..33335107 100644 --- a/include/unixstl/unixstl.h +++ b/include/unixstl/unixstl.h @@ -5,7 +5,7 @@ * platform discriminations, and definitions of types. * * Created: 15th January 2002 - * Updated: 17th September 2026 + * Updated: 10th October 2026 * * Home: http://stlsoft.org/ * @@ -50,8 +50,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_MAJOR 3 # define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_MINOR 11 -# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_REVISION 3 -# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_EDIT 130 +# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_REVISION 5 +# define UNIXSTL_VER_UNIXSTL_H_UNIXSTL_EDIT 132 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /** \file unixstl/unixstl.h @@ -163,12 +163,13 @@ # define _UNIXSTL_VER_1_8_6_B04 0x01080684 /*!< Version 1.8.8 beta 4 (with STLSoft 1.11.1 beta 8) */ # define _UNIXSTL_VER_1_8_6_B05 0x01080685 /*!< Version 1.8.8 beta 5 (with STLSoft 1.11.1 rc 2) */ # define _UNIXSTL_VER_1_8_6_B06 0x01080686 /*!< Version 1.8.8 beta 6 (with STLSoft 1.11.1 rc 3) */ +# define _UNIXSTL_VER_1_8_6_B07 0x01080687 /*!< Version 1.8.8 beta 7 (with STLSoft 1.11.1 rc 8) */ #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ #define _UNIXSTL_VER_MAJOR 1 #define _UNIXSTL_VER_MINOR 8 #define _UNIXSTL_VER_PATCH 6 -#define _UNIXSTL_VER_ALPHABETA 0x86 +#define _UNIXSTL_VER_ALPHABETA 0x87 #define _UNIXSTL_VER \ (0\ @@ -199,8 +200,8 @@ * STLSoft version compatibility check(s) */ -#if _STLSOFT_VER < 0x010b0187 -# error This version of the UNIXSTL libraries requires STLSoft version 1.11.1 beta 7, or later +#if _STLSOFT_VER < 0x010B01C8 +# error This version of the UNIXSTL libraries requires STLSoft version 1.11.1 rc 8, or later #endif /* _STLSOFT_VER */ diff --git a/include/winstl/filesystem/filesystem_traits.hpp b/include/winstl/filesystem/filesystem_traits.hpp index fa1f9f8a..fe194b8a 100644 --- a/include/winstl/filesystem/filesystem_traits.hpp +++ b/include/winstl/filesystem/filesystem_traits.hpp @@ -149,7 +149,7 @@ */ #ifndef WINSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) /* There is no stlsoft namespace, so must define ::winstl */ namespace winstl @@ -973,7 +973,7 @@ struct filesystem_traits typedef DWORD error_type; typedef DWORD file_attributes_type; private: -#if !defined(STLSOFT_COMPILER_IS_MSVC) || \ +#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\ _MSC_VER >= 1200 typedef STLSOFT_NS_QUAL(auto_buffer) buffer_type_; #endif /* compiler */ @@ -1696,9 +1696,10 @@ struct filesystem_traits { return WINSTL_CONST_MAX_PATH; } +#if 0 +#elif defined(STLSOFT_COMPILER_IS_MSVC) &&\ + _MSC_VER < 1200 -#if defined(STLSOFT_COMPILER_IS_MSVC) && \ - _MSC_VER < 1200 static size_type get_full_path_name( @@ -2236,7 +2237,7 @@ struct filesystem_traits WINSTL_ASSERT(NULL != path); WINSTL_ASSERT(NULL != stat_data); -#if !defined(STLSOFT_COMPILER_IS_MSVC) || \ +#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\ _MSC_VER >= 1200 size_type const len = class_type::str_len(path); @@ -2594,7 +2595,7 @@ struct filesystem_traits typedef DWORD error_type; typedef DWORD file_attributes_type; private: -#if !defined(STLSOFT_COMPILER_IS_MSVC) || \ +#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\ _MSC_VER >= 1200 typedef STLSOFT_NS_QUAL(auto_buffer) buffer_type_; #endif /* compiler */ @@ -3596,7 +3597,7 @@ struct filesystem_traits WINSTL_ASSERT(NULL != path); WINSTL_ASSERT(NULL != stat_data); -#if !defined(STLSOFT_COMPILER_IS_MSVC) || \ +#if !defined(STLSOFT_COMPILER_IS_MSVC) ||\ _MSC_VER >= 1200 size_type const len = class_type::str_len(path); @@ -3933,7 +3934,7 @@ struct filesystem_traits */ #ifndef WINSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) } // namespace winstl # else diff --git a/include/winstl/process/functions.h b/include/winstl/process/functions.h index ed7f362b..b5915821 100644 --- a/include/winstl/process/functions.h +++ b/include/winstl/process/functions.h @@ -4,11 +4,11 @@ * Purpose: Process functions. * * Created: 12th March 2006 - * Updated: 24th December 2024 + * Updated: 8th October 2026 * * Home: http://stlsoft.org/ * - * Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems + * Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems * Copyright (c) 2006-2019, Matthew Wilson and Synesis Software * All rights reserved. * @@ -52,9 +52,9 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_MAJOR 1 -# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_MINOR 1 -# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_REVISION 7 -# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_EDIT 35 +# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_MINOR 2 +# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_REVISION 1 +# define WINSTL_VER_WINSTL_PROCESS_H_FUNCTIONS_EDIT 37 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ @@ -69,6 +69,11 @@ # pragma message(__FILE__) #endif /* STLSOFT_TRACE_INCLUDE */ +#ifndef STLSOFT_INCL_H_PROCESS +# define STLSOFT_INCL_H_PROCESS +# include +#endif /* !STLSOFT_INCL_H_PROCESS */ + #ifndef STLSOFT_INCL_STLSOFT_QUALITY_H_CONTRACT # include #endif /* !STLSOFT_INCL_STLSOFT_QUALITY_H_CONTRACT */ @@ -144,6 +149,8 @@ winstl_C_CreateProcess9_a( , LPPROCESS_INFORMATION pi ) { + /* apply Null Object (Variable) pattern */ + STARTUPINFO si_; PROCESS_INFORMATION pi_; BOOL b; @@ -158,7 +165,18 @@ winstl_C_CreateProcess9_a( pi = &pi_; } - b = STLSOFT_NS_GLOBAL(CreateProcessA)(NULL, stlsoft_const_cast(ws_char_a_t*, cmdLine), processAttributes, threadAttributes, inheritsHandles, creationFlags, stlsoft_const_cast(ws_char_a_t*, environment), currentDirectory, si, pi); + b = STLSOFT_NS_GLOBAL(CreateProcessA)( + NULL + , stlsoft_const_cast(ws_char_a_t*, cmdLine) + , processAttributes + , threadAttributes + , inheritsHandles + , creationFlags + , stlsoft_const_cast(ws_char_a_t*, environment) + , currentDirectory + , si + , pi + ); if (b) { @@ -228,6 +246,21 @@ winstl_C_CreateProcess0A( return winstl_C_CreateProcessEA(cmdLine, NULL); } +/** + * \brief Obtains the id of the calling process. + * + * \ingroup group__library__System + * + * \return The process id, as obtained from \c _getpid(), which equals + * \c GetCurrentProcessId() cast to \c int. + */ +STLSOFT_INLINE +int +winstl_C_get_current_process_id(void) +{ + return STLSOFT_NS_GLOBAL(_getpid)(); +} + /* ///////////////////////////////////////////////////////////////////////// * obsolete functions diff --git a/include/winstl/system/system_traits.hpp b/include/winstl/system/system_traits.hpp index 74b0bc17..bc65f0af 100644 --- a/include/winstl/system/system_traits.hpp +++ b/include/winstl/system/system_traits.hpp @@ -82,8 +82,9 @@ # include #endif /* !STLSOFT_INCL_STLSOFT_INTERNAL_H_SAFESTR */ -#if defined(WINSTL_OS_IS_WIN64) || \ - defined(_Wp64) +#if 0 +#elif defined(WINSTL_OS_IS_WIN64) ||\ + defined(_Wp64) # define _WINSTL_SYSTEM_TRAITS_USE_TRUNCATION_TESTING #endif /* _WIN64 || _M_IA64 */ @@ -150,7 +151,7 @@ */ #ifndef WINSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) /* There is no stlsoft namespace, so must define ::winstl */ namespace winstl @@ -1854,7 +1855,7 @@ struct system_traits */ #ifndef WINSTL_NO_NAMESPACE -# if defined(STLSOFT_NO_NAMESPACE) || \ +# if defined(STLSOFT_NO_NAMESPACE) ||\ defined(STLSOFT_DOCUMENTATION_SKIP_SECTION) } // namespace winstl # else diff --git a/include/winstl/winstl.h b/include/winstl/winstl.h index 361f6292..592659ec 100644 --- a/include/winstl/winstl.h +++ b/include/winstl/winstl.h @@ -5,7 +5,7 @@ * platform discriminations, and definitions of types. * * Created: 15th January 2002 - * Updated: 30th September 2026 + * Updated: 8th October 2026 * * Home: http://stlsoft.org/ * @@ -50,8 +50,8 @@ #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION # define WINSTL_VER_WINSTL_H_WINSTL_MAJOR 3 # define WINSTL_VER_WINSTL_H_WINSTL_MINOR 18 -# define WINSTL_VER_WINSTL_H_WINSTL_REVISION 14 -# define WINSTL_VER_WINSTL_H_WINSTL_EDIT 244 +# define WINSTL_VER_WINSTL_H_WINSTL_REVISION 15 +# define WINSTL_VER_WINSTL_H_WINSTL_EDIT 245 #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ /** \file winstl/winstl.h @@ -184,12 +184,13 @@ # define _WINSTL_VER_1_13_0_B01 0x010d0081 /*!< Version 1.13.0 beta 1 (with STLSoft 1.11.1 rc 3) */ # define _WINSTL_VER_1_13_0_B02 0x010d0082 /*!< Version 1.13.0 beta 2 (with STLSoft 1.11.1 rc 4) */ # define _WINSTL_VER_1_13_0_B03 0x010d0083 /*!< Version 1.13.0 beta 3 (with STLSoft 1.11.1 rc 7) */ +# define _WINSTL_VER_1_13_0_B04 0x010d0084 /*!< Version 1.13.0 beta 4 (with STLSoft 1.11.1 rc 8) */ #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */ #define _WINSTL_VER_MAJOR 1 #define _WINSTL_VER_MINOR 13 #define _WINSTL_VER_PATCH 0 -#define _WINSTL_VER_ALPHABETA 0x83 +#define _WINSTL_VER_ALPHABETA 0x84 #define _WINSTL_VER \ (0\ @@ -290,8 +291,8 @@ * STLSoft version compatibility check(s) */ -#if _STLSOFT_VER < 0x010b0151 -# error This version of the WinSTL libraries requires STLSoft version 1.11.1 alpha 17, or later +#if _STLSOFT_VER < 0x010B01C8 +# error This version of the WinSTL libraries requires STLSoft version 1.11.1 rc 8, or later #endif /* _STLSOFT_VER */ diff --git a/prepare_cmake.sh b/prepare_cmake.sh index b1f2da31..6852f32c 100755 --- a/prepare_cmake.sh +++ b/prepare_cmake.sh @@ -1,41 +1,146 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") -CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -if [[ -n "$MSYSTEM" ]]; then +# ########################################################## +# functions - 1 - DefaultMakeCmd=mingw32-make.exe - MinGW=1 -else +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in - DefaultMakeCmd=make -fi -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 - +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +BuildSharedLibs=0 Configuration=Release ExamplesDisabled=0 +MinGW=$(sis_cmake_is_truey "${SIS_CMAKE_MINGW:-}" && echo 1 || echo 0) MSVC_MT=0 -MinGW="${MinGW:=0}" NO_ACE=0 NO_cstring=0 NO_shwild=0 RunMake=0 +SisUseColours=0 +STLSoftDirGiven= TestingDisabled=0 USE_UNIXem=0 VerboseMakefile=0 +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + + # ########################################################## # command-line handling while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; + --build-shared-libs) + + BuildSharedLibs=1 + ;; --cmake-verbose-makefile|-v) VerboseMakefile=1 @@ -76,6 +181,11 @@ while [[ $# -gt 0 ]]; do RunMake=1 ;; + --stlsoft-root-dir|-s) + + shift + STLSoftDirGiven=$1 + ;; --use-unixem) USE_UNIXem=1 @@ -86,41 +196,43 @@ while [[ $# -gt 0 ]]; do cat << EOF Creates/reinitialises the CMake build script(s) -$ScriptPath [ ... flags/options ... ] - -IMPORTANT: If you have not previously performed a CMake installation of xTests, the library that STLSoft uses for its own tests, you MUST specify the -E and -T flags. Once you have installed STLSoft, you can then configure/build/install STLSoft, and then come back and prepare STLSoft without -E/-T and exercise its examples and tests. +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + --build-shared-libs + builds ${ProjectName} as a shared library (BUILD_SHARED_LIBS=ON) + -v --cmake-verbose-makefile - configures CMake to run verbosely (by setting CMAKE_VERBOSE_MAKEFILE - to be ON) + configures CMake to run verbosely (CMAKE_VERBOSE_MAKEFILE=ON) -d --debug-configuration - use Debug configuration (by setting CMAKE_BUILD_TYPE=Debug). Default - is to use Release + use Debug configuration (CMAKE_BUILD_TYPE=Debug). Default is Release -E --disable-examples - disables building of examples (by setting BUILD_EXAMPLES=OFF) + disables building of examples (BUILD_EXAMPLES=OFF) -T --disable-testing - disables building of tests (by setting BUILD_TESTING=OFF). This is - necessary, for example, when installing on a system that does not - (yet) have xTests - which itself depends on STLSOFT - installed + disables building of tests (BUILD_TESTING=OFF). Unless testing is + disabled the STLSoft and xTests libraries will be required to be + available to CMake --mingw uses explicitly the "MinGW Makefiles" generator --msvc-mt - when using Visual C++ (MSVC), the static runtime library will be - selected; the default is the dynamic runtime library. Has no effect - when not using Visual C++ + when using Visual C++ (MSVC), select the static runtime library --no-ace suppresses discovery of ACE (ACESTL examples/tests will not be built) @@ -129,17 +241,24 @@ Flags/options: suppresses discovery of cstring package --no-shwild - suppresses discovery of shwild package + prevents recognising shwild library (NO_SHWILD=ON); xTests + pattern-match assertions are then unavailable -m --run-make - executes make after a successful running of CMake + executes a build via cmake --build after a successful configure + + -s + --stlsoft-root-dir + specifies the STLSoft root-directory, which will be passed to CMake + as the variable STLSOFT, and which will override the environment + variable STLSOFT (if present) --use-unixem - when building on Windows, use the UNIXem library and define the - preprocessor symbol _STLSOFT_FORCE_ANY_COMPILER so as to emulate and - exercise UNIXSTL, not WinSTL (or COMSTL, etc.). Has no effect when not - executing on Windows + when building on Windows, use the UNIXem library and define the + preprocessor symbol _STLSOFT_FORCE_ANY_COMPILER so as to emulate and + exercise UNIXSTL, not WinSTL (or COMSTL, etc.). Has no effect when + not executing on Windows standard flags: @@ -147,15 +266,13 @@ Flags/options: --help displays this help and terminates -IMPORTANT: If you have not previously performed a CMake installation of xTests, the library that STLSoft uses for its own tests, you MUST specify the -E and -T flags. Once you have installed STLSoft, you can then configure/build/install STLSoft, and then come back and prepare STLSoft without -E/-T and exercise its examples and tests. - EOF exit 0 ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -168,72 +285,74 @@ done # ########################################################## # main() -mkdir -p $CMakeDir || exit 1 +mkdir -p "$CMakeDir" || exit 1 -cd $CMakeDir - -echo "Executing CMake for ${ProjectName} (in ${CMakeDir})" +echo +echo "Executing CMake for ${ProjectNameClr} (in ${CMakeDirClr})" +if [ $BuildSharedLibs -eq 0 ]; then CMakeBuildSharedLibsFlag="OFF" ; else CMakeBuildSharedLibsFlag="ON" ; fi if [ $ExamplesDisabled -eq 0 ]; then CMakeBuildExamplesFlag="ON" ; else CMakeBuildExamplesFlag="OFF" ; fi if [ $MSVC_MT -eq 0 ]; then CMakeMsvcMtFlag="OFF" ; else CMakeMsvcMtFlag="ON" ; fi if [ $NO_ACE -eq 0 ]; then CMakeNoACE="OFF" ; else CMakeNoACE="ON" ; fi if [ $NO_cstring -eq 0 ]; then CMakeNoCstring="OFF" ; else CMakeNoCstring="ON" ; fi if [ $NO_shwild -eq 0 ]; then CMakeNoShwild="OFF" ; else CMakeNoShwild="ON" ; fi +if [ -z "$STLSoftDirGiven" ]; then CMakeSTLSoftVariable="" ; else CMakeSTLSoftVariable="-DSTLSOFT=$STLSoftDirGiven/" ; fi if [ $TestingDisabled -eq 0 ]; then CMakeBuildTestingFlag="ON" ; else CMakeBuildTestingFlag="OFF" ; fi if [ $USE_UNIXem -ne 0 ]; then CMakeUSE_UNIXem="ON" ; else CMakeUSE_UNIXem="OFF" ; fi if [ $VerboseMakefile -eq 0 ]; then CMakeVerboseMakefileFlag="OFF" ; else CMakeVerboseMakefileFlag="ON" ; fi +# NOTE: the generator is the *only* thing that may differ between the MinGW +# and the default paths; every -D option is passed in both cases, so that no +# flag can be silently ignored according to the generator selected. + +CMakeGeneratorArgs=() + +if [ -n "${SIS_CMAKE_GENERATOR:-}" ] && [ $MinGW -eq 0 ]; then + + CMakeGeneratorArgs=(-G "$SIS_CMAKE_GENERATOR") +fi + if [ $MinGW -ne 0 ]; then - cmake \ - -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \ - -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \ - -DCMAKE_BUILD_TYPE=$Configuration \ - -DNO_ACE:BOOL=$CMakeNoACE \ - -DNO_CSTRING:BOOL=$CMakeNoCstring \ - -DNO_SHWILD:BOOL=$CMakeNoShwild \ - -DUSE_UNIXEM:BOOL=$CMakeUSE_UNIXem \ - -G "MinGW Makefiles" \ - -S $Dir \ - -B $CMakeDir \ - || (cd ->/dev/null ; exit 1) -else - - cmake \ - -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \ - -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \ - -DCMAKE_BUILD_TYPE=$Configuration \ - -DCMAKE_VERBOSE_MAKEFILE:BOOL=$CMakeVerboseMakefileFlag \ - -DMSVC_USE_MT:BOOL=$CMakeMsvcMtFlag \ - -DNO_ACE:BOOL=$CMakeNoACE \ - -DNO_CSTRING:BOOL=$CMakeNoCstring \ - -DNO_SHWILD:BOOL=$CMakeNoShwild \ - -DUSE_UNIXEM:BOOL=$CMakeUSE_UNIXem \ - -S $Dir \ - -B $CMakeDir \ - || (cd ->/dev/null ; exit 1) + CMakeGeneratorArgs=(-G "MinGW Makefiles") fi +cmake \ + -DBUILD_EXAMPLES:BOOL=$CMakeBuildExamplesFlag \ + -DBUILD_SHARED_LIBS:BOOL=$CMakeBuildSharedLibsFlag \ + -DBUILD_TESTING:BOOL=$CMakeBuildTestingFlag \ + -DCMAKE_BUILD_TYPE=$Configuration \ + -DCMAKE_VERBOSE_MAKEFILE:BOOL=$CMakeVerboseMakefileFlag \ + -DMSVC_USE_MT:BOOL=$CMakeMsvcMtFlag \ + -DNO_ACE:BOOL=$CMakeNoACE \ + -DNO_CSTRING:BOOL=$CMakeNoCstring \ + -DNO_SHWILD:BOOL=$CMakeNoShwild \ + -DUSE_UNIXEM:BOOL=$CMakeUSE_UNIXem \ + $CMakeSTLSoftVariable \ + "${CMakeGeneratorArgs[@]}" \ + -B "$CMakeDir" \ + -S "$Dir" \ + || exit 1 + status=0 if [ $RunMake -ne 0 ]; then - echo "Executing build for ${ProjectName} (via command \`$MakeCmd\`)" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build)" - $MakeCmd + sis_cmake_build status=$? fi -cd ->/dev/null - if [ $VerboseMakefile -ne 0 ]; then - echo -e "contents of $CMakeDir:" - ls -al $CMakeDir + echo + echo -e "contents of ${CMakeDirClr}:" + ls -al "$CMakeDir" fi exit $status # ############################## end of file ############################# # - diff --git a/remove_cmake_artefacts.sh b/remove_cmake_artefacts.sh index 97c1bc00..377d3ab8 100755 --- a/remove_cmake_artefacts.sh +++ b/remove_cmake_artefacts.sh @@ -1,17 +1,20 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} Directories=( CMakeFiles Testing cmake - docs examples projects src @@ -21,19 +24,77 @@ Files=( CMakeCache.txt CTestTestfile.cmake DartConfiguration.tcl - Doxyfile Makefile cmake_install.cmake install_manifest.txt ) +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" # ########################################################## # operating environment detection -OsName="$(uname -s)" +OsName="$(uname -s 2>/dev/null || echo Unknown)" case "${OsName}" in - CYGWIN*|MINGW*|MSYS_NT*) + CYGWIN*|MINGW*|MSYS_NT*|Windows_NT) Directories+=( ARM64 @@ -45,9 +106,6 @@ case "${OsName}" in "*.sln" "*.vcxproj" ) - ;; - *) - ;; esac @@ -58,18 +116,27 @@ esac while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF Removes all known CMake artefacts -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + standard flags: @@ -82,7 +149,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -97,59 +164,50 @@ done if [ ! -d "$CMakeDir" ]; then - echo "$ScriptPath: CMake build directory '$CMakeDir' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" + echo "${ScriptPathClr}: CMake build directory '${CMakeDirClr}' not found so nothing to do; use script 'prepare_cmake.sh' if you wish to prepare CMake artefacts" exit 0 -else - - echo "Removing all ${ProjectName} cmake artefacts in '$CMakeDir'" +fi - num_dirs_removed=0 - num_files_removed=0 +echo "Removing all ${ProjectNameClr} cmake artefacts in '${CMakeDirClr}'" - for d in ${Directories[@]} - do +num_dirs_removed=0 +num_files_removed=0 - fq_dir_path="$CMakeDir/$d" +for d in "${Directories[@]}"; do - [ -d "$fq_dir_path" ] || continue + fq_dir_path="$CMakeDir/$d" - echo "removing directory '$d'" + [ -d "$fq_dir_path" ] || continue - rm -dfr "$fq_dir_path" + echo "removing directory '$d'" - num_dirs_removed=$((num_dirs_removed+1)) - done + rm -dfr "$fq_dir_path" - cd "$CMakeDir" + num_dirs_removed=$((num_dirs_removed+1)) +done - for f in ${Files[@]} - do +for f in "${Files[@]}"; do - for fq_file_path in $f - do + for fq_file_path in "$CMakeDir"/$f; do - [ -f "$fq_file_path" ] || continue + [ -f "$fq_file_path" ] || continue - echo "removing file '$fq_file_path'" + echo "removing file '$fq_file_path'" - rm -f "$fq_file_path" + rm -f "$fq_file_path" - num_files_removed=$((num_files_removed+1)) - done + num_files_removed=$((num_files_removed+1)) done +done - cd ->/dev/null - - if [ 0 -eq $num_dirs_removed ] && [ 0 -eq $num_files_removed ]; then +if [ 0 -eq $num_dirs_removed ] && [ 0 -eq $num_files_removed ]; then - echo "nothing to do" - else + echo "nothing to do" +else - echo "removed $num_dirs_removed directories and $num_files_removed files" - fi + echo "removed $num_dirs_removed directories and $num_files_removed files" fi # ############################## end of file ############################# # - diff --git a/run_all_automated_tests.cmd b/run_all_automated_tests.cmd new file mode 100644 index 00000000..b6dd937d --- /dev/null +++ b/run_all_automated_tests.cmd @@ -0,0 +1,106 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] + +SET UnitOnly=0 +SET ComponentOnly=0 + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) automated test programs ^(unit and component^) ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + --unit-only ^ + + runs only unit-test programs ^ + +^ + + --component-only ^ + + runs only component-test programs ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE IF /I {--unit-only}=={%%a} ( + + SET UnitOnly=1 + ) ELSE IF /I {--component-only}=={%%a} ( + + SET ComponentOnly=1 + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +IF %UnitOnly% NEQ 0 IF %ComponentOnly% NEQ 0 ( + + ECHO %SCRIPT_PATH_DOC%: --unit-only and --component-only are mutually exclusive 1>&2 + + EXIT /B 1 +) + +IF %UnitOnly% NEQ 0 ( + CALL "%SCRIPT_DIRECTORY%run_all_unit_tests.cmd" + EXIT /B %ERRORLEVEL% +) + +IF %ComponentOnly% NEQ 0 ( + CALL "%SCRIPT_DIRECTORY%run_all_component_tests.cmd" + EXIT /B %ERRORLEVEL% +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +ECHO Running all %ProjectName% automated test programs ^(unit and component^) + +CALL "%SCRIPT_DIRECTORY%run_all_unit_tests.cmd" +IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% + +CALL "%SCRIPT_DIRECTORY%run_all_component_tests.cmd" +EXIT /B %ERRORLEVEL% + +ENDLOCAL diff --git a/run_all_automated_tests.sh b/run_all_automated_tests.sh new file mode 100755 index 00000000..2c13b16e --- /dev/null +++ b/run_all_automated_tests.sh @@ -0,0 +1,180 @@ +#! /bin/bash + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +ComponentOnly=0 +ForwardedArgs=() +SisUseColours=0 +UnitOnly=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - handled above; forward so category runners see it + ForwardedArgs+=("$1") + ;; + --unit-only) + + UnitOnly=1 + ;; + --component-only) + + ComponentOnly=1 + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Runs all (matching) automated test programs (unit and component) + +${ScriptPath} [ ... flags/options ... ] + +Flags/options: + + behaviour: + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + --component-only + runs only component-test programs + + --unit-only + runs only unit-test programs + + (all other flags are forwarded to the category runner script) + + + standard flags: + + --help + displays this help and terminates + +EOF + + exit 0 + ;; + *) + + ForwardedArgs+=("$1") + ;; + esac + + shift +done + +if [ $UnitOnly -ne 0 ] && [ $ComponentOnly -ne 0 ]; then + + >&2 echo "${ScriptPathClr}: ${SisClr_Red}${SisClr_Bold}--unit-only${SisClr_None} and ${SisClr_Red}${SisClr_Bold}--component-only${SisClr_None} are mutually exclusive" + + exit 1 +fi + + +# ########################################################## +# main() + +status=0 + +if [ $UnitOnly -ne 0 ]; then + + "$Dir/run_all_unit_tests.sh" "${ForwardedArgs[@]}" + exit $? +fi + +if [ $ComponentOnly -ne 0 ]; then + + "$Dir/run_all_component_tests.sh" "${ForwardedArgs[@]}" + exit $? +fi + +echo +echo "Running all ${ProjectNameClr} automated test programs (unit and component)" + +"$Dir/run_all_unit_tests.sh" "${ForwardedArgs[@]}" +status=$? + +if [ $status -eq 0 ]; then + + "$Dir/run_all_component_tests.sh" "${ForwardedArgs[@]}" + status=$? +fi + +exit $status + + +# ############################## end of file ############################# # diff --git a/run_all_component_tests.cmd b/run_all_component_tests.cmd new file mode 100644 index 00000000..970c9bab --- /dev/null +++ b/run_all_component_tests.cmd @@ -0,0 +1,94 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] + +IF DEFINED SIS_CMAKE_BUILD_DIR ( + + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% +) ELSE ( + + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build +) + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) component-test programs ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + --component-only ^ + + accepted for compatibility; this script always runs component tests only ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE IF /I {--component-only}=={%%a} ( + + REM Benign: this script is already component-only + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + + EXIT /B 1 +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +ECHO Running all %ProjectName% component-test programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*component.*\.exe$`) DO ( + + ECHO . + ECHO executing %%f + %%f + IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% +) + +ENDLOCAL diff --git a/run_all_component_tests.sh b/run_all_component_tests.sh new file mode 100755 index 00000000..7473fc54 --- /dev/null +++ b/run_all_component_tests.sh @@ -0,0 +1,303 @@ +#! /bin/bash + +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +ListOnly=0 +RunMake=1 +SisUseColours=0 +Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; + --component-only) + + # Benign: this script is already component-only (aggregate / CI may + # pass it) + ;; + --list-only|-l) + + ListOnly=1 + ;; + --no-make|-M) + + RunMake=0 + ;; + --verbosity) + + shift + Verbosity=$1 + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Runs all (matching) component-test programs + +${ScriptPath} [ ... flags/options ... ] + +Flags/options: + + behaviour: + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + --component-only + accepted for compatibility; this script always runs component tests + only + + -l + --list-only + lists the target programs but does not execute them + + -M + --no-make + does not execute a build before running programs + + --verbosity + specifies an explicit verbosity, forwarded to each program + + + standard flags: + + --help + displays this help and terminates + +EOF + + exit 0 + ;; + *) + + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" + + exit 1 + ;; + esac + + shift +done + + +# ########################################################## +# main() + +status=0 + +if [ $RunMake -ne 0 ]; then + + if [ $ListOnly -eq 0 ]; then + + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all component-test programs" + + mkdir -p "$CMakeDir" || exit 1 + + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" + + exit 1 + fi + + sis_cmake_build + status=$? + fi +else + + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" + + exit 1 + fi +fi + +if [ $status -eq 0 ]; then + + if [ $ListOnly -ne 0 ]; then + + echo + echo "Listing all ${ProjectNameClr} component-test programs" + else + + echo + echo "Running all ${ProjectNameClr} component-test programs" + fi + + NumPrograms=0 + + while IFS= read -r -d '' f; do + + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac + + NumPrograms=$((NumPrograms + 1)) + + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" + + if [ $ListOnly -ne 0 ]; then + + echo "would execute ${fClr}:" + + continue + fi + + if [ $Verbosity -ge 3 ]; then + + echo + fi + if [ $Verbosity -ge 2 ]; then + + echo "executing ${fClr}:" + fi + + if "$f" --verbosity="$Verbosity"; then + + : + else + + status=$? + + break 1 + fi + done < <(find "$CMakeDir" -type f \( -name 'test_component*' -o -name 'test.component.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + >&2 echo "${ScriptPathClr}: no matching executable component-test programs under '${CMakeDirClr}' (execute bits missing after artifact download?)" + + if [ $ListOnly -eq 0 ]; then + + exit 1 + fi + fi +fi + +exit $status + + +# ############################## end of file ############################# # diff --git a/run_all_examples.sh b/run_all_examples.sh index 09362e68..8744843c 100755 --- a/run_all_examples.sh +++ b/run_all_examples.sh @@ -1,39 +1,120 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 +SisUseColours=0 SkipInteractive=0 # ########################################################## # colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi -if command -v tput > /dev/null; then + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} - SisClr_Red=${FG_BLUE:-$(tput setaf 1)} SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} SisClr_None=${FD_NONE:-$(tput sgr0)} -else - - SisClr_Blue= - SisClr_Red= - SisClr_Bold= - SisClr_None= + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} fi +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + # ########################################################## -# helpers +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} example_stem() { @@ -58,6 +139,8 @@ names_match() [ "$a" = "$b" ] } +# Examples listed in .github/ci_skip_interactive_examples.txt require an +# interactive desktop (GUI dialogs, etc.) and would hang a headless CI run. is_skipped_interactive_example() { local name f_norm line line_stem skip_file="$Dir/.github/ci_skip_interactive_examples.txt" @@ -66,7 +149,7 @@ is_skipped_interactive_example() if [ ! -f "$skip_file" ]; then - >&2 echo "$ScriptPath: --skip-interactive: skip list not found at '$skip_file'" + >&2 echo "${ScriptPathClr}: --skip-interactive: skip list not found at '$skip_file'" return 1 fi @@ -100,6 +183,10 @@ is_skipped_interactive_example() while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --list-only|-l) ListOnly=1 @@ -118,19 +205,24 @@ while [[ $# -gt 0 ]]; do cat << EOF Runs all (matching) example programs -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + -l --list-only lists the target programs but does not execute them -M --no-make - does not execute CMake and make before running examples + does not execute a build before running programs --skip-interactive skips examples listed in .github/ci_skip_interactive_examples.txt @@ -149,7 +241,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -168,22 +260,26 @@ if [ $RunMake -ne 0 ]; then if [ $ListOnly -eq 0 ]; then - echo "Executing build (via command \`$MakeCmd\`) and then running all ${ProjectName} example programs" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all example programs" - mkdir -p $CMakeDir || exit 1 + mkdir -p "$CMakeDir" || exit 1 - cd $CMakeDir + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - $MakeCmd - status=$? + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" - cd ->/dev/null + exit 1 + fi + + sis_cmake_build + status=$? fi else - if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step" + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" exit 1 fi @@ -191,19 +287,25 @@ fi if [ $status -eq 0 ]; then - if [ ! -d "$CMakeDir/examples" ]; then + if [ $ListOnly -ne 0 ]; then - >&2 echo "$ScriptPath: examples build tree not found at '$CMakeDir/examples'" + echo + echo "Listing all ${ProjectNameClr} example programs" + else - exit 1 + echo + echo "Running all ${ProjectNameClr} example programs" fi - if [ $ListOnly -ne 0 ]; then + # Examples that require human input may honour SIS_EXAMPLE_SMOKE for a + # no-arg built-in tmpfile demo (see example.c.cstring_vector). + export SIS_EXAMPLE_SMOKE=1 - echo "Listing all ${ProjectName} example programs" - else + if [ ! -d "$CMakeDir/examples" ]; then + + >&2 echo "${ScriptPathClr}: examples build tree not found at '${CMakeDirClr}/examples'" - echo "Running all ${ProjectName} example programs" + exit 1 fi # Exclude CMake / build artefacts that can become +x after artifact restore. @@ -227,7 +329,7 @@ if [ $status -eq 0 ]; then if [ ${#ExamplePrograms[@]} -eq 0 ]; then - >&2 echo "$ScriptPath: no matching executable example programs under '$CMakeDir/examples' (execute bits missing after artifact download?)" + >&2 echo "${ScriptPathClr}: no matching executable example programs under '${CMakeDirClr}/examples' (execute bits missing after artifact download?)" if [ $ListOnly -eq 0 ]; then @@ -238,16 +340,17 @@ if [ $status -eq 0 ]; then for f in "${ExamplePrograms[@]}" do + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" + if is_skipped_interactive_example "$f"; then if [ $ListOnly -ne 0 ]; then - echo "would skip $SisClr_Blue$SisClr_Bold$f$SisClr_None (interactive; --skip-interactive)" - + echo "would skip ${fClr} (interactive; --skip-interactive)" else echo - echo "skipping $SisClr_Blue$SisClr_Bold$f$SisClr_None (interactive; --skip-interactive)" + echo "skipping ${fClr} (interactive; --skip-interactive)" fi continue @@ -255,15 +358,15 @@ if [ $status -eq 0 ]; then if [ $ListOnly -ne 0 ]; then - echo "would execute $SisClr_Blue$SisClr_Bold$f$SisClr_None:" + echo "would execute ${fClr}:" continue fi echo - echo "executing $SisClr_Blue$SisClr_Bold$f$SisClr_None:" + echo "executing ${fClr}:" - if $f; then + if "$f"; then : else diff --git a/run_all_performance_tests.cmd b/run_all_performance_tests.cmd new file mode 100644 index 00000000..adb8746f --- /dev/null +++ b/run_all_performance_tests.cmd @@ -0,0 +1,96 @@ +@echo off + +SETLOCAL + +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] + +IF DEFINED SIS_CMAKE_BUILD_DIR ( + + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% +) ELSE ( + + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build +) + +FOR %%a IN (%*) DO ( + + IF /I {--help}=={%%a} ( + + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ + +Runs all ^(matching^) performance-test programs ^ + +^ + +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ + +^ + +Flags/options: ^ + + behaviour: ^ + +^ + + --gap-groups ^ + + sets SIS_PERFTESTS_GROUPGAPS=1 so performance programs emit a ^ + + blank line between scenario groups ^ + +^ + + standard flags: ^ + +^ + + --help ^ + + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE IF /I {--gap-groups}=={%%a} ( + + SET SIS_PERFTESTS_GROUPGAPS=1 + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) +) + +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + + EXIT /B 1 +) + +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" + +IF NOT DEFINED ProjectName ( + + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 +) + +ECHO Running all %ProjectName% performance-test programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*performance.*\.exe$`) DO ( + + ECHO . + ECHO executing %%f + %%f + IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% +) + +ENDLOCAL diff --git a/run_all_performance_tests.sh b/run_all_performance_tests.sh new file mode 100755 index 00000000..6f3c3739 --- /dev/null +++ b/run_all_performance_tests.sh @@ -0,0 +1,290 @@ +#! /bin/bash + +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) +CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} +ProjectNameFile="$Dir/.sis/project_name.txt" +ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 + +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} +GapGroups=0 +ListOnly=0 +RunMake=1 +SisUseColours=0 + + +# ########################################################## +# colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then + + SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} + SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} + SisClr_None=${FD_NONE:-$(tput sgr0)} + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} +fi + +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + + +# ########################################################## +# command-line handling + +while [[ $# -gt 0 ]]; do + + case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; + --gap-groups) + + GapGroups=1 + ;; + --list-only|-l) + + ListOnly=1 + ;; + --no-make|-M) + + RunMake=0 + ;; + --help) + + [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" + cat << EOF +Runs all (matching) performance-test programs + +${ScriptPath} [ ... flags/options ... ] + +Flags/options: + + behaviour: + + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + + --gap-groups + sets SIS_PERFTESTS_GROUPGAPS=1 so performance programs emit a blank + line between scenario groups + + -l + --list-only + lists the target programs but does not execute them + + -M + --no-make + does not execute a build before running programs + + + standard flags: + + --help + displays this help and terminates + +EOF + + exit 0 + ;; + *) + + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" + + exit 1 + ;; + esac + + shift +done + + +# ########################################################## +# main() + +if [ $GapGroups -ne 0 ]; then + + export SIS_PERFTESTS_GROUPGAPS=1 +fi + +status=0 + +if [ $RunMake -ne 0 ]; then + + if [ $ListOnly -eq 0 ]; then + + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all performance-test programs" + + mkdir -p "$CMakeDir" || exit 1 + + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" + + exit 1 + fi + + sis_cmake_build + status=$? + fi +else + + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" + + exit 1 + fi +fi + +if [ $status -eq 0 ]; then + + if [ $ListOnly -ne 0 ]; then + + echo + echo "Listing all ${ProjectNameClr} performance-test programs" + else + + echo + echo "Running all ${ProjectNameClr} performance-test programs" + fi + + NumPrograms=0 + + while IFS= read -r -d '' f; do + + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac + + NumPrograms=$((NumPrograms + 1)) + + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" + + if [ $ListOnly -ne 0 ]; then + + echo "would execute ${fClr}:" + + continue + fi + + echo + echo "executing ${fClr}:" + + if "$f"; then + + : + else + + status=$? + + break 1 + fi + done < <(find "$CMakeDir" -type f \( -name 'test_performance*' -o -name 'test.performance.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + echo "${ScriptPathClr}: found no performance-test programs under '${CMakeDirClr}' (none found)" + + exit 0 + fi +fi + +exit $status + + +# ############################## end of file ############################# # diff --git a/run_all_scratch_tests.cmd b/run_all_scratch_tests.cmd index 4b18544b..9b05623f 100644 --- a/run_all_scratch_tests.cmd +++ b/run_all_scratch_tests.cmd @@ -1,173 +1,85 @@ -@ECHO OFF +@echo off -SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS +SETLOCAL -SET ScriptPath=%~n0[%~x0] -SET ScriptDir=%~dp0 +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] IF DEFINED SIS_CMAKE_BUILD_DIR ( - SET CMakeDir=%SIS_CMAKE_BUILD_DIR% + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% ) ELSE ( - SET CMakeDir=%ScriptDir%_build + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build ) -SET ListOnly=0 -SET RunMake=1 -SET TestVerbosityDefault=3 -SET TestVerbosity=%TEST_VERBOSITY% +FOR %%a IN (%*) DO ( -IF NOT DEFINED TestVerbosity SET TestVerbosity=%TestVerbosityDefault% -IF DEFINED XTESTS_VERBOSITY SET TestVerbosity=%XTESTS_VERBOSITY% + IF /I {--help}=={%%a} ( -SET BuildConfig=Release -IF DEFINED CMAKE_BUILD_TYPE SET BuildConfig=%CMAKE_BUILD_TYPE% + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ -:: ######################################################### -:: command-line handling +Runs all ^(matching^) scratch-test programs ^ -:parse_args -IF "%~1"=="" GOTO args_done +^ -IF /I "%~1"=="--help" GOTO show_help -IF /I "%~1"=="-l" ( - SET ListOnly=1 - GOTO next_arg -) -IF /I "%~1"=="--list-only" ( - SET ListOnly=1 - GOTO next_arg -) -IF /I "%~1"=="-M" ( - SET RunMake=0 - GOTO next_arg -) -IF /I "%~1"=="--no-make" ( - SET RunMake=0 - GOTO next_arg -) -IF /I "%~1"=="--verbosity" ( - SHIFT - IF "%~1"=="" ( - ECHO %ScriptPath%: --verbosity requires an argument 1>&2 - EXIT /B 1 - ) - SET TestVerbosity=%~1 - GOTO next_arg -) +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ -ECHO %ScriptPath%: unrecognised argument '%~1'; use --help for usage 1>&2 -EXIT /B 1 +^ -:next_arg -SHIFT -GOTO parse_args +Flags/options: ^ -:args_done + behaviour: ^ +^ -:: ######################################################### -:: main() + standard flags: ^ -SET status=0 +^ -IF %RunMake% NEQ 0 ( - IF %ListOnly% EQU 0 ( - ECHO Executing build and then running all scratch test programs - CALL :do_build - IF ERRORLEVEL 1 SET status=1 - ) -) ELSE ( - IF NOT EXIST "%CMakeDir%\CMakeCache.txt" ( - ECHO %ScriptPath%: cannot run in '--no-make' mode without a previous successful build step 1>&2 - EXIT /B 1 - ) -) + --help ^ + + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE ( -IF %status% EQU 0 ( - IF %ListOnly% NEQ 0 ( - ECHO Listing all scratch test programs - ) ELSE ( - ECHO Running all scratch test programs - ) - - FOR /F "usebackq delims=" %%f IN (`DIR /A:-D /B /S "%CMakeDir%" 2^>NUL ^| FINDSTR /I /R "test[._]scratch[._].*\.exe$"`) DO ( - IF !ListOnly! NEQ 0 ( - ECHO would execute %%f: - ) ELSE ( - IF !TestVerbosity! GEQ 3 ECHO. - IF !TestVerbosity! GEQ 2 ECHO executing %%f: - CALL "%%f" - IF ERRORLEVEL 1 SET status=1 - ) - ) + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) ) -IF !status! NEQ 0 ( - ENDLOCAL +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + EXIT /B 1 ) -ENDLOCAL -EXIT /B 0 +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" -:: ######################################################### -:: helpers +IF NOT DEFINED ProjectName ( -:do_build -IF DEFINED SIS_CMAKE_MAKE_COMMAND ( - PUSHd "%CMakeDir%" - CALL %SIS_CMAKE_MAKE_COMMAND% - SET build_status=!ERRORLEVEL! - POPD - EXIT /B !build_status! -) -IF EXIST "%CMakeDir%\Makefile" ( - PUSHd "%CMakeDir%" - IF DEFINED MSYSTEM ( - mingw32-make.exe - ) ELSE ( - make - ) - SET build_status=!ERRORLEVEL! - POPD - EXIT /B !build_status! + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 ) -cmake --build "%CMakeDir%" --config %BuildConfig% -EXIT /B %ERRORLEVEL% +ECHO Running all %ProjectName% scratch-test programs + +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*scratch.*\.exe$`) DO ( -:show_help -IF EXIST "%ScriptDir%.sis\script_info_lines.txt" ( - type "%ScriptDir%.sis\script_info_lines.txt" + ECHO . + ECHO executing %%f + %%f + IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% ) -ECHO. -ECHO Runs all (matching) scratch-test programs -ECHO. -ECHO %ScriptPath% [ ... flags/options ... ] -ECHO. -ECHO Flags/options: -ECHO. -ECHO behaviour: -ECHO. -ECHO -l -ECHO --list-only -ECHO lists the target programs but does not execute them -ECHO. -ECHO -M -ECHO --no-make -ECHO does not execute CMake and make before running tests -ECHO. -ECHO --verbosity ^ -ECHO reserved for output control; scratch programs are invoked without xTests verbosity flags -ECHO. -ECHO standard flags: -ECHO. -ECHO --help -ECHO displays this help and terminates -EXIT /B 0 - - -:: ############################# end of file ############################ :: + +ENDLOCAL diff --git a/run_all_scratch_tests.sh b/run_all_scratch_tests.sh index 14626b2e..5e6659a8 100755 --- a/run_all_scratch_tests.sh +++ b/run_all_scratch_tests.sh @@ -1,36 +1,120 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 -Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} +SisUseColours=0 # ########################################################## # colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done -if command -v tput > /dev/null; then +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then + + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} - SisClr_Red=${FG_BLUE:-$(tput setaf 1)} SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} SisClr_None=${FD_NONE:-$(tput sgr0)} -else - - SisClr_Blue= - SisClr_Red= - SisClr_Bold= - SisClr_None= + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} fi +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + # ########################################################## # command-line handling @@ -38,6 +122,10 @@ fi while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --list-only|-l) ListOnly=1 @@ -46,34 +134,30 @@ while [[ $# -gt 0 ]]; do RunMake=0 ;; - --verbosity) - - shift - Verbosity=$1 - ;; --help) [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF Runs all (matching) scratch-test programs -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + -l --list-only lists the target programs but does not execute them -M --no-make - does not execute CMake and make before running tests - - --verbosity - specifies an explicit verbosity (reserved; scratch programs are - invoked without xTests verbosity flags) + does not execute a build before running programs standard flags: @@ -87,7 +171,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -105,22 +189,27 @@ status=0 if [ $RunMake -ne 0 ]; then if [ $ListOnly -eq 0 ]; then - echo "Executing build (via command \`$MakeCmd\`) and then running all ${ProjectName} scratch test programs" - mkdir -p $CMakeDir || exit 1 + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all scratch-test programs" - cd $CMakeDir + mkdir -p "$CMakeDir" || exit 1 - $MakeCmd - status=$? + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then + + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" - cd ->/dev/null + exit 1 + fi + + sis_cmake_build + status=$? fi else - if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step" + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" exit 1 fi @@ -130,43 +219,58 @@ if [ $status -eq 0 ]; then if [ $ListOnly -ne 0 ]; then - echo "Listing all ${ProjectName} scratch test programs" + echo + echo "Listing all ${ProjectNameClr} scratch-test programs" else - echo "Running all ${ProjectName} scratch test programs" + echo + echo "Running all ${ProjectNameClr} scratch-test programs" fi - for f in $(find "$CMakeDir" -type f '(' -name 'test_scratch*' -o -name 'test.scratch.*' ')' -exec test -x {} \; -print | sort) - do + NumPrograms=0 - if [ $ListOnly -ne 0 ]; then + while IFS= read -r -d '' f; do - echo "would execute $SisClr_Blue$SisClr_Bold$f$SisClr_None:" + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac - continue - fi + NumPrograms=$((NumPrograms + 1)) - if [ $Verbosity -ge 3 ]; then + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" - echo - fi - if [ $Verbosity -ge 2 ]; then + if [ $ListOnly -ne 0 ]; then + + echo "would execute ${fClr}:" - echo "executing $SisClr_Blue$SisClr_Bold$f$SisClr_None:" + continue fi - if $f; then + echo + echo "executing ${fClr}:" + + if "$f"; then : else status=$? + + break 1 fi - done + done < <(find "$CMakeDir" -type f \( -name 'test_scratch*' -o -name 'test.scratch.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + echo "${ScriptPathClr}: found no scratch-test programs under '${CMakeDirClr}' (none found)" + + exit 0 + fi fi exit $status # ############################## end of file ############################# # - diff --git a/run_all_unit_tests.cmd b/run_all_unit_tests.cmd index de418c98..18b040a8 100644 --- a/run_all_unit_tests.cmd +++ b/run_all_unit_tests.cmd @@ -1,209 +1,94 @@ -@ECHO OFF +@echo off -SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS +SETLOCAL -SET ScriptPath=%~n0[%~x0] -SET ScriptDir=%~dp0 +SET SCRIPT_DIRECTORY=%~dp0 +SET SCRIPT_PATH_DOC=%~n0[%~x0] IF DEFINED SIS_CMAKE_BUILD_DIR ( - SET CMakeDir=%SIS_CMAKE_BUILD_DIR% + SET CMAKE_DIR=%SIS_CMAKE_BUILD_DIR% ) ELSE ( - SET CMakeDir=%ScriptDir%_build + SET CMAKE_DIR=%SCRIPT_DIRECTORY%_build ) -SET ListOnly=0 -SET RunMake=1 -SET UnitOnly=0 -SET ComponentOnly=0 -SET TestVerbosityDefault=3 -SET TestVerbosity=%TEST_VERBOSITY% +FOR %%a IN (%*) DO ( -IF NOT DEFINED TestVerbosity SET TestVerbosity=%TestVerbosityDefault% -IF DEFINED XTESTS_VERBOSITY SET TestVerbosity=%XTESTS_VERBOSITY% + IF /I {--help}=={%%a} ( -SET BuildConfig=Release -IF DEFINED CMAKE_BUILD_TYPE SET BuildConfig=%CMAKE_BUILD_TYPE% + IF EXIST "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" ( + type "%SCRIPT_DIRECTORY%.sis\script_info_lines.txt" + ) + ECHO ^ -:: ######################################################### -:: command-line handling +Runs all ^(matching^) unit-test programs ^ -:parse_args -IF "%~1"=="" GOTO args_done +^ -IF /I "%~1"=="--help" GOTO show_help -IF /I "%~1"=="-l" ( - SET ListOnly=1 - GOTO next_arg -) -IF /I "%~1"=="--list-only" ( - SET ListOnly=1 - GOTO next_arg -) -IF /I "%~1"=="-M" ( - SET RunMake=0 - GOTO next_arg -) -IF /I "%~1"=="--no-make" ( - SET RunMake=0 - GOTO next_arg -) -IF /I "%~1"=="--unit-only" ( - SET UnitOnly=1 - GOTO next_arg -) -IF /I "%~1"=="--component-only" ( - SET ComponentOnly=1 - GOTO next_arg -) -IF /I "%~1"=="--verbosity" ( - SHIFT - IF "%~1"=="" ( - ECHO %ScriptPath%: --verbosity requires an argument 1>&2 - EXIT /B 1 - ) - SET TestVerbosity=%~1 - GOTO next_arg -) +%SCRIPT_PATH_DOC% [ ... flags/options ... ] ^ -ECHO %ScriptPath%: unrecognised argument '%~1'; use --help for usage 1>&2 -EXIT /B 1 +^ -:next_arg -SHIFT -GOTO parse_args +Flags/options: ^ -:args_done + behaviour: ^ -IF %UnitOnly% NEQ 0 IF %ComponentOnly% NEQ 0 ( - ECHO %ScriptPath%: --unit-only and --component-only are mutually exclusive 1>&2 - EXIT /B 1 -) +^ -IF %UnitOnly% NEQ 0 ( - SET TestKindDescription=unit test - SET FindPattern=test[._]unit[._].*\.exe$ -) ELSE IF %ComponentOnly% NEQ 0 ( - SET TestKindDescription=component test - SET FindPattern=test[._]component[._].*\.exe$ -) ELSE ( - SET TestKindDescription=component and unit test - SET FindPattern=test[._]unit[._].*\.exe$ test[._]component[._].*\.exe$ -) + --unit-only ^ + accepted for compatibility; this script always runs unit tests only ^ -:: ######################################################### -:: main() +^ -SET status=0 + standard flags: ^ -IF %RunMake% NEQ 0 ( - IF %ListOnly% EQU 0 ( - ECHO Executing build and then running all !TestKindDescription! programs - CALL :do_build - IF ERRORLEVEL 1 SET status=1 - ) -) ELSE ( - IF NOT EXIST "%CMakeDir%\CMakeCache.txt" ( - ECHO %ScriptPath%: cannot run in '--no-make' mode without a previous successful build step 1>&2 - EXIT /B 1 - ) -) +^ + + --help ^ -IF %status% EQU 0 ( - IF %ListOnly% NEQ 0 ( - ECHO Listing all !TestKindDescription! programs - ) ELSE ( - ECHO Running all !TestKindDescription! programs - ) - - FOR /F "usebackq delims=" %%f IN (`DIR /A:-D /B /S "%CMakeDir%" 2^>NUL ^| FINDSTR /I /R "%FindPattern%"`) DO ( - IF !ListOnly! NEQ 0 ( - ECHO would execute %%f: - ) ELSE ( - ECHO. - ECHO executing %%f: - CALL "%%f" --verbosity=!TestVerbosity! - IF ERRORLEVEL 1 ( - SET status=1 - GOTO tests_done - ) - ) - ) + displays this help and terminates ^ + + + EXIT /B 0 + ) ELSE IF /I {--unit-only}=={%%a} ( + + REM Benign: this script is already unit-only + ) ELSE ( + + ECHO "%SCRIPT_DIRECTORY%: unrecognised argument '%%a'; use --help for usage" 1>&2 + + EXIT /B 1 + ) ) -:tests_done -IF !status! NEQ 0 ( - ENDLOCAL +if NOT EXIST "%CMAKE_DIR%" ( + + ECHO "CMake build directory '%CMAKE_DIR%' does not exist" + EXIT /B 1 ) -ENDLOCAL -EXIT /B 0 +SET "ProjectName=" +FOR /F "usebackq delims=" %%p IN ("%SCRIPT_DIRECTORY%.sis\project_name.txt") DO SET "ProjectName=%%p" -:: ######################################################### -:: helpers +IF NOT DEFINED ProjectName ( -:do_build -IF DEFINED SIS_CMAKE_MAKE_COMMAND ( - PUSHd "%CMakeDir%" - CALL %SIS_CMAKE_MAKE_COMMAND% - SET build_status=!ERRORLEVEL! - POPD - EXIT /B !build_status! -) -IF EXIST "%CMakeDir%\Makefile" ( - PUSHd "%CMakeDir%" - IF DEFINED MSYSTEM ( - mingw32-make.exe - ) ELSE ( - make - ) - SET build_status=!ERRORLEVEL! - POPD - EXIT /B !build_status! + ECHO %SCRIPT_PATH_DOC%: could not read project name from .sis\project_name.txt 1>&2 + + EXIT /B 1 ) -cmake --build "%CMakeDir%" --config %BuildConfig% -EXIT /B %ERRORLEVEL% +ECHO Running all %ProjectName% unit-test programs -:show_help -IF EXIST "%ScriptDir%.sis\script_info_lines.txt" ( - type "%ScriptDir%.sis\script_info_lines.txt" +FOR /F "usebackq" %%f IN (`DIR /A:-D /B /S "%CMAKE_DIR%" ^| FINDSTR /I test.*unit.*\.exe$`) DO ( + + ECHO . + ECHO executing %%f + %%f + IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% ) -ECHO. -ECHO Runs all (matching) unit-test and/or component-test programs -ECHO. -ECHO %ScriptPath% [ ... flags/options ... ] -ECHO. -ECHO Flags/options: -ECHO. -ECHO behaviour: -ECHO. -ECHO -l -ECHO --list-only -ECHO lists the target programs but does not execute them -ECHO. -ECHO -M -ECHO --no-make -ECHO does not execute CMake and make before running tests -ECHO. -ECHO --unit-only -ECHO runs only unit-test programs (test.unit.* / test_unit*) -ECHO. -ECHO --component-only -ECHO runs only component-test programs (test.component.* / test_component*) -ECHO. -ECHO --verbosity ^ -ECHO specifies an explicit verbosity for the unit-test(s) -ECHO. -ECHO standard flags: -ECHO. -ECHO --help -ECHO displays this help and terminates -EXIT /B 0 - - -:: ############################# end of file ############################ :: + +ENDLOCAL diff --git a/run_all_unit_tests.sh b/run_all_unit_tests.sh index 0d7e147d..56003827 100755 --- a/run_all_unit_tests.sh +++ b/run_all_unit_tests.sh @@ -1,38 +1,121 @@ #! /bin/bash -ScriptPath=$0 -Dir=$(cd $(dirname "$ScriptPath"); pwd) -Basename=$(basename "$ScriptPath") +# ########################################################## +# functions - 1 + +sis_cmake_is_truey() { + case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in + + 1|ok|on|true|yes|y) + + return 0 + ;; + *) + + return 1 + ;; + esac +} + + +# ########################################################## +# constants and variables + +Basename=$(basename "$0") +Dir=$(cd "$(dirname "$0")" && pwd) CMakeDir=${SIS_CMAKE_BUILD_DIR:-$Dir/_build} -[[ -n "$MSYSTEM" ]] && DefaultMakeCmd=mingw32-make.exe || DefaultMakeCmd=make -MakeCmd=${SIS_CMAKE_MAKE_COMMAND:-${SIS_CMAKE_COMMAND:-$DefaultMakeCmd}} ProjectNameFile="$Dir/.sis/project_name.txt" ProjectName=$(tr -d '[:space:]' < "$ProjectNameFile") +ScriptPath=$0 +AlwaysUseColours=${SIS_CMAKE_ALWAYS_USE_COLOURS:-${SIS_ALWAYS_USE_COLOURS:-0}} ListOnly=0 RunMake=1 -UnitOnly=0 -ComponentOnly=0 +SisUseColours=0 Verbosity=${XTESTS_VERBOSITY:-${TEST_VERBOSITY:-3}} # ########################################################## # colours +# +# Enable when tput is available and either: +# - AlwaysUseColours is set (overrides NO_COLOR; may set TERM if +# empty/dumb), or +# - NO_COLOR is unset, stdout is a TTY, and TERM is not dumb (union of +# collect-c's "TERM set + TTY" and cstring's "TTY" — empty TERM on a +# TTY is OK). + +SisClr_Blue= +SisClr_Bold= +SisClr_Green= +SisClr_None= +SisClr_Red= +SisClr_Yellow= + +for arg in "$@"; do + + case $arg in + --always-use-colors|--always-use-colours|-A) + + AlwaysUseColours=1 + ;; + esac +done + +if command -v tput >/dev/null 2>&1; then + + if [ $AlwaysUseColours -ne 0 ]; then -if command -v tput > /dev/null; then + if [ -z "${TERM:-}" ] || [ "$TERM" = "dumb" ]; then + + TERM=xterm-256color + fi + + SisUseColours=1 + elif [ -z "${NO_COLOR:-}" ] && [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then + + SisUseColours=1 + fi +fi + +if [ $SisUseColours -ne 0 ]; then SisClr_Blue=${FG_BLUE:-$(tput setaf 4)} - SisClr_Red=${FG_BLUE:-$(tput setaf 1)} SisClr_Bold=${FD_BOLD:-$(tput bold)} + SisClr_Green=${FG_GREEN:-$(tput setaf 2)} SisClr_None=${FD_NONE:-$(tput sgr0)} -else - - SisClr_Blue= - SisClr_Red= - SisClr_Bold= - SisClr_None= + SisClr_Red=${FG_RED:-$(tput setaf 1)} + SisClr_Yellow=${FG_YELLOW:-$(tput setaf 3)} fi +CMakeDirClr="${SisClr_Blue}${SisClr_Bold}${CMakeDir}${SisClr_None}" +ProjectNameClr="${SisClr_Blue}${SisClr_Bold}${ProjectName}${SisClr_None}" +ScriptPathClr="${SisClr_Blue}${SisClr_Bold}${ScriptPath}${SisClr_None}" + + +# ########################################################## +# functions - 2 + +sis_cmake_build() { + + local config="${SIS_CMAKE_CONFIG:-Release}" + local args=(--build "$CMakeDir") + if [ -f "$CMakeDir/CMakeCache.txt" ] && grep -q '^CMAKE_CONFIGURATION_TYPES:' "$CMakeDir/CMakeCache.txt" 2>/dev/null; then + + args+=(--config "$config") + fi + if [ "$#" -gt 0 ]; then + + local t + for t in "$@"; do + + args+=(--target "$t") + done + fi + + cmake "${args[@]}" +} + # ########################################################## # command-line handling @@ -40,6 +123,10 @@ fi while [[ $# -gt 0 ]]; do case $1 in + --always-use-colors|--always-use-colours|-A) + + # AlwaysUseColours=1 - this is handled by the for loop above + ;; --list-only|-l) ListOnly=1 @@ -50,11 +137,7 @@ while [[ $# -gt 0 ]]; do ;; --unit-only) - UnitOnly=1 - ;; - --component-only) - - ComponentOnly=1 + # Benign: this script is already unit-only (aggregate / CI may pass it) ;; --verbosity) @@ -65,30 +148,32 @@ while [[ $# -gt 0 ]]; do [ -f "$Dir/.sis/script_info_lines.txt" ] && cat "$Dir/.sis/script_info_lines.txt" cat << EOF -Runs all (matching) unit-test and/or component-test programs +Runs all (matching) unit-test programs -$ScriptPath [ ... flags/options ... ] +${ScriptPath} [ ... flags/options ... ] Flags/options: behaviour: + -A + --always-use-colors + --always-use-colours + forces use of colours even when stdout is not a TTY + -l --list-only lists the target programs but does not execute them -M --no-make - does not execute CMake and make before running tests + does not execute a build before running programs --unit-only - runs only unit-test programs (test.unit.* / test_unit*) - - --component-only - runs only component-test programs (test.component.* / test_component*) + accepted for compatibility; this script always runs unit tests only --verbosity - specifies an explicit verbosity for the unit-test(s) + specifies an explicit verbosity, forwarded to each program standard flags: @@ -102,7 +187,7 @@ EOF ;; *) - >&2 echo "$ScriptPath: unrecognised argument '$1'; use --help for usage" + >&2 echo "${ScriptPathClr}: unrecognised argument '${SisClr_Red}${SisClr_Bold}$1${SisClr_None}'; use --help for usage" exit 1 ;; @@ -111,50 +196,36 @@ EOF shift done -if [ $UnitOnly -ne 0 ] && [ $ComponentOnly -ne 0 ]; then - - >&2 echo "$ScriptPath: --unit-only and --component-only are mutually exclusive" - - exit 1 -fi - # ########################################################## # main() status=0 -if [ $UnitOnly -ne 0 ]; then - - TestKindDescription='unit test' -elif [ $ComponentOnly -ne 0 ]; then - - TestKindDescription='component test' -else - - TestKindDescription='component and unit test' -fi - if [ $RunMake -ne 0 ]; then if [ $ListOnly -eq 0 ]; then - echo "Executing build (via command \`$MakeCmd\`) and then running all ${ProjectName} ${TestKindDescription} programs" + echo + echo "Executing build of ${ProjectNameClr} (via cmake --build) and then running all unit-test programs" - mkdir -p $CMakeDir || exit 1 + mkdir -p "$CMakeDir" || exit 1 - cd $CMakeDir + if [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - $MakeCmd - status=$? + >&2 echo "${ScriptPathClr}: '${CMakeDirClr}' is not configured; use prepare_cmake.sh first" - cd ->/dev/null + exit 1 + fi + + sis_cmake_build + status=$? fi else - if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ] || [ ! -d "$CMakeDir/CMakeFiles" ]; then + if [ ! -d "$CMakeDir" ] || [ ! -f "$CMakeDir/CMakeCache.txt" ]; then - >&2 echo "$ScriptPath: cannot run in '--no-make' mode without a previous successful build step" + >&2 echo "${ScriptPathClr}: cannot run in '--no-make' mode without a previous successful configure/build" exit 1 fi @@ -164,54 +235,31 @@ if [ $status -eq 0 ]; then if [ $ListOnly -ne 0 ]; then - echo "Listing all ${ProjectName} ${TestKindDescription} programs" + echo + echo "Listing all ${ProjectNameClr} unit-test programs" else - echo "Running all ${ProjectName} ${TestKindDescription} programs" + echo + echo "Running all ${ProjectNameClr} unit-test programs" fi - if [ $UnitOnly -ne 0 ]; then + NumPrograms=0 - find_name_expr=( \( -name 'test_unit*' -o -name 'test.unit.*' \) ) - elif [ $ComponentOnly -ne 0 ]; then + while IFS= read -r -d '' f; do - find_name_expr=( \( -name 'test_component*' -o -name 'test.component.*' \) ) - else - - find_name_expr=( \( -name 'test_unit*' -o -name 'test.unit.*' -o -name 'test_component*' -o -name 'test.component.*' \) ) - fi - - # Exclude build artefacts that can match suite name globs (CMake object - # files under CMakeFiles/, etc.). - TestPrograms=( $(find "$CMakeDir" -type f "${find_name_expr[@]}" \ - ! -path '*/CMakeFiles/*' \ - ! -name '*.a' \ - ! -name '*.d' \ - ! -name '*.lib' \ - ! -name '*.log' \ - ! -name '*.o' \ - ! -name '*.obj' \ - ! -name '*.pdb' \ - -exec test -x {} \; -print | sort) ) - - echo "discovered ${#TestPrograms[@]} ${TestKindDescription} program(s)" + case "$f" in + *.pdb|*.ilk|*.log|*.obj|*.o) + continue + ;; + esac - if [ ${#TestPrograms[@]} -eq 0 ]; then - - >&2 echo "$ScriptPath: no matching executable ${TestKindDescription} programs under '$CMakeDir' (execute bits missing after artifact download?)" - - if [ $ListOnly -eq 0 ]; then - - status=1 - fi - fi + NumPrograms=$((NumPrograms + 1)) - for f in "${TestPrograms[@]}" - do + fClr="${SisClr_Blue}${SisClr_Bold}${f}${SisClr_None}" if [ $ListOnly -ne 0 ]; then - echo "would execute $SisClr_Blue$SisClr_Bold$f$SisClr_None:" + echo "would execute ${fClr}:" continue fi @@ -222,10 +270,10 @@ if [ $status -eq 0 ]; then fi if [ $Verbosity -ge 2 ]; then - echo "executing $SisClr_Blue$SisClr_Bold$f$SisClr_None:" + echo "executing ${fClr}:" fi - if $f --verbosity=$Verbosity; then + if "$f" --verbosity="$Verbosity"; then : else @@ -234,11 +282,20 @@ if [ $status -eq 0 ]; then break 1 fi - done + done < <(find "$CMakeDir" -type f \( -name 'test_unit*' -o -name 'test.unit.*' \) \( -perm -100 -o -name '*.exe' \) -print0 2>/dev/null | sort -z) + + if [ $NumPrograms -eq 0 ]; then + + >&2 echo "${ScriptPathClr}: no matching executable unit-test programs under '${CMakeDirClr}' (execute bits missing after artifact download?)" + + if [ $ListOnly -eq 0 ]; then + + exit 1 + fi + fi fi exit $status # ############################## end of file ############################# # - diff --git a/test/component/stlsoft/system/CMakeLists.txt b/test/component/stlsoft/system/CMakeLists.txt index 071c04bc..452572ef 100644 --- a/test/component/stlsoft/system/CMakeLists.txt +++ b/test/component/stlsoft/system/CMakeLists.txt @@ -1,3 +1,4 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten add_subdirectory(environment_block) add_subdirectory(environment_variable_exists) +add_subdirectory(environment_variable_strtoll) diff --git a/test/component/stlsoft/system/environment_variable_exists/entry.cpp b/test/component/stlsoft/system/environment_variable_exists/entry.cpp index 93e4506a..6fbb2d09 100644 --- a/test/component/stlsoft/system/environment_variable_exists/entry.cpp +++ b/test/component/stlsoft/system/environment_variable_exists/entry.cpp @@ -4,7 +4,7 @@ * Purpose: Component-tests for `stlsoft::environment_variable_exists()`. * * Created: 28th September 2026 - * Updated: 30th September 2026 + * Updated: 8th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -218,21 +218,49 @@ static void TEST_environment_variable_exists_WITH_simple_string() } #ifndef _WIN32 -/* An empty value is a defined variable on UNIX. The Windows CRT treats an - * empty assignment as erasure, so this case is UNIX-only. +/* An empty value is a defined variable on UNIX: getenv() returns "". + * Existence is true for the C function and the C++ overloads. The Windows + * CRT treats an empty assignment as erasure, so this case is UNIX-only. */ static void TEST_environment_variable_exists_WHEN_EMPTY() { - environment_variable_scope const empty(VAR_NAME, ""); + char const* const name = VAR_NAME; - char const* const value = ::getenv(VAR_NAME); + { + environment_variable_scope const empty(VAR_NAME, ""); - XTESTS_REQUIRE(TEST_PTR_NE(NULL, value)); - TEST_MS_EQ("", value); + char const* const value = ::getenv(name); - char const* const name = VAR_NAME; + XTESTS_REQUIRE(TEST_PTR_NE(NULL, value)); + TEST_MS_EQ("", value); - TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); + TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name)); + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(std::string(name))); + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(stlsoft::simple_string(name))); + TEST_BOOLEAN_FALSE(stlsoft::stlsoft_C_environment_variable_exists_m("STLSOFT_CT_ENV_VAR_EXISTS_NO_SUCH")); + } + + { + environment_variable_scope const present(VAR_NAME, "present"); + + TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name)); + + environment_variable_scope const emptied(VAR_NAME, ""); + + char const* const value = ::getenv(name); + + XTESTS_REQUIRE(TEST_PTR_NE(NULL, value)); + TEST_MS_EQ("", value); + + TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name)); + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); + } + + environment_variable_scope const erased(VAR_NAME, ss_nullptr_k); + + TEST_BOOLEAN_FALSE(stlsoft::stlsoft_C_environment_variable_exists_m(name)); + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name)); } #endif /* !_WIN32 */ diff --git a/test/component/stlsoft/system/environment_variable_strtoll/CMakeLists.txt b/test/component/stlsoft/system/environment_variable_strtoll/CMakeLists.txt new file mode 100644 index 00000000..ecaa534c --- /dev/null +++ b/test/component/stlsoft/system/environment_variable_strtoll/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.component.stlsoft.system.environment_variable_strtoll entry.cpp) diff --git a/test/component/stlsoft/system/environment_variable_strtoll/entry.cpp b/test/component/stlsoft/system/environment_variable_strtoll/entry.cpp new file mode 100644 index 00000000..1cc60278 --- /dev/null +++ b/test/component/stlsoft/system/environment_variable_strtoll/entry.cpp @@ -0,0 +1,404 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/component/stlsoft/system/environment_variable_strtoll/entry.cpp + * + * Purpose: Component tests for + * `stlsoft_C_environment_variable_strtoll_m()`. + * + * Created: 28th September 2026 + * Updated: 8th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include +#include + +/* Standard C header files */ +#include +#include +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +namespace { + + static void TEST_environment_variable_strtoll_WHEN_ABSENT(); + static void TEST_environment_variable_strtoll_WHEN_DECIMAL(); + static void TEST_environment_variable_strtoll_WHEN_BASE_PREFIXES(); + static void TEST_environment_variable_strtoll_WHEN_LEADING_WHITESPACE(); + static void TEST_environment_variable_strtoll_WHEN_NOT_COMPLETE(); + static void TEST_environment_variable_strtoll_WHEN_OUT_OF_RANGE(); + static void TEST_environment_variable_strtoll_WHEN_LONG_VALUE(); + static void TEST_environment_variable_strtoll_WHEN_ERASED(); + static void TEST_environment_variable_strtoll_WHEN_VALUE_REPLACED(); + static void TEST_environment_variable_strtoll_EXACT_NAME(); + static void TEST_environment_variable_strtoll_CASE_SENSITIVITY(); + static void TEST_environment_variable_strtoll_CPP_WRAPPER(); +#ifndef _WIN32 + static void TEST_environment_variable_strtoll_WHEN_EMPTY(); +#endif /* !_WIN32 */ +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char* argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.component.stlsoft.system.environment_variable_strtoll", verbosity)) + { + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_ABSENT); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_DECIMAL); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_BASE_PREFIXES); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_LEADING_WHITESPACE); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_NOT_COMPLETE); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_OUT_OF_RANGE); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_LONG_VALUE); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_ERASED); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_VALUE_REPLACED); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_EXACT_NAME); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_CASE_SENSITIVITY); + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_CPP_WRAPPER); +#ifndef _WIN32 + XTESTS_RUN_CASE(TEST_environment_variable_strtoll_WHEN_EMPTY); +#endif /* !_WIN32 */ + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * constants + */ + +namespace { + + using platformstl::environment_variable_scope; + + stlsoft::ss_sint64_t const SENTINEL_ = 0x5a5a5a5a; + + char const VAR_NAME[] = "STLSOFT_CT_ENV_VAR_STRTOLL"; + char const VAR_NAME_EXTRA[] = "STLSOFT_CT_ENV_VAR_STRTOLL_EXTRA"; + char const VAR_NAME_LOWER[] = "stlsoft_ct_env_var_strtoll_case"; + char const VAR_NAME_PREFIX[] = "STLSOFT_CT_ENV_VAR_STRTOL"; + char const VAR_NAME_UPPER[] = "STLSOFT_CT_ENV_VAR_STRTOLL_CASE"; +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * macros + */ + +#define TEST_READ_PARSED_(name, expected) \ + \ + do \ + { \ + stlsoft::ss_sint64_t result_ = SENTINEL_; \ + \ + errno = EPERM; \ + \ + stlsoft::ss_truthy_t const parsed_ = \ + stlsoft::stlsoft_C_environment_variable_strtoll_m( \ + (name) \ + , &result_ \ + ); \ + \ + TEST_BOOLEAN_TRUE(parsed_); \ + TEST_INT_EQ((expected), result_); \ + TEST_INT_EQ(0, errno); \ + } while (0) + +#define TEST_READ_REJECTED_(name, expected_errno) \ + \ + do \ + { \ + stlsoft::ss_sint64_t result_ = SENTINEL_; \ + \ + errno = EPERM; \ + \ + stlsoft::ss_truthy_t const parsed_ = \ + stlsoft::stlsoft_C_environment_variable_strtoll_m( \ + (name) \ + , &result_ \ + ); \ + \ + TEST_BOOLEAN_FALSE(parsed_); \ + TEST_INT_EQ(0, result_); \ + TEST_INT_EQ((expected_errno), errno); \ + } while (0) + +#define TEST_PARSED_(value, expected) \ + \ + do \ + { \ + environment_variable_scope const scope_(VAR_NAME, (value)); \ + \ + TEST_READ_PARSED_(VAR_NAME, (expected)); \ + } while (0) + +#define TEST_REJECTED_(value, expected_errno) \ + \ + do \ + { \ + environment_variable_scope const scope_(VAR_NAME, (value)); \ + \ + TEST_READ_REJECTED_(VAR_NAME, (expected_errno)); \ + } while (0) + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +namespace { + +static void fill_n_( + char* buff +, int n +, char ch +) +{ + int i; + + for (i = 0; i != n; ++i) + { + buff[i] = ch; + } + + buff[n] = '\0'; +} + +/* Absence is a failed lookup: the function returns 0, sets errno to EINVAL, + * and writes 0 through result. + */ +static void TEST_environment_variable_strtoll_WHEN_ABSENT() +{ + environment_variable_scope const absent(VAR_NAME, ss_nullptr_k); + + TEST_READ_REJECTED_(VAR_NAME, EINVAL); +} + +static void TEST_environment_variable_strtoll_WHEN_DECIMAL() +{ + TEST_PARSED_("0", 0); + TEST_PARSED_("00", 0); + TEST_PARSED_("+0", 0); + TEST_PARSED_("-0", 0); + TEST_PARSED_("1", 1); + TEST_PARSED_("42", 42); + TEST_PARSED_("+42", 42); + TEST_PARSED_("-42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_(" -42", -42); + TEST_PARSED_("-123456", -123456); + + TEST_PARSED_("9223372036854775807", STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807)); + TEST_PARSED_("-9223372036854775807", -STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807)); + TEST_PARSED_("-9223372036854775808", -STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807) - 1); +} + +/* Base 0 follows strtoll(): a leading 0 selects octal and a leading 0x + * selects hexadecimal. The whole value must be one subject sequence. + */ +static void TEST_environment_variable_strtoll_WHEN_BASE_PREFIXES() +{ + TEST_PARSED_("010", 8); + TEST_PARSED_("+010", 8); + TEST_PARSED_("-010", -8); + TEST_PARSED_("0x10", 16); + TEST_PARSED_("0X10", 16); + TEST_PARSED_("0xff", 255); + TEST_PARSED_("0xFf", 255); + TEST_PARSED_("+0x10", 16); + TEST_PARSED_("-0x10", -16); + TEST_PARSED_("0x0", 0); +} + +/* Leading whitespace belongs to a strtoll() subject sequence. Trailing + * characters, including whitespace, do not. + */ +static void TEST_environment_variable_strtoll_WHEN_LEADING_WHITESPACE() +{ + TEST_PARSED_(" 42", 42); + TEST_PARSED_("\t42", 42); + TEST_PARSED_(" \t -42", -42); + TEST_PARSED_(" +0x10", 16); + + TEST_REJECTED_("42 ", EINVAL); + TEST_REJECTED_(" 42 ", EINVAL); + TEST_REJECTED_("42\n", EINVAL); +} + +static void TEST_environment_variable_strtoll_WHEN_NOT_COMPLETE() +{ + TEST_REJECTED_("abc", EINVAL); + TEST_REJECTED_(" ", EINVAL); + TEST_REJECTED_("+", EINVAL); + TEST_REJECTED_("-", EINVAL); + TEST_REJECTED_("++1", EINVAL); + TEST_REJECTED_("--1", EINVAL); + TEST_REJECTED_("0x", EINVAL); + TEST_REJECTED_("0xG", EINVAL); + TEST_REJECTED_("123abc", EINVAL); + TEST_REJECTED_("08", EINVAL); + TEST_REJECTED_("09", EINVAL); + TEST_REJECTED_("1.5", EINVAL); + TEST_REJECTED_("1e2", EINVAL); +} + +static void TEST_environment_variable_strtoll_WHEN_OUT_OF_RANGE() +{ + /* Out-of-range values are rejected with ERANGE and result is set to 0, + * rather than the saturated limits that strtoll() returns. + */ + TEST_REJECTED_("9223372036854775808", ERANGE); + TEST_REJECTED_("-9223372036854775809", ERANGE); +} + +/* The complete environment-variable value is read before parsing, so a + * valid value remains valid when preceded by substantial whitespace. + */ +static void TEST_environment_variable_strtoll_WHEN_LONG_VALUE() +{ + char text[1024]; + + fill_n_(text, 1000, ' '); + text[1000] = '-'; + text[1001] = '4'; + text[1002] = '2'; + text[1003] = '\0'; + TEST_PARSED_(text, -42); + + text[1003] = 'x'; + text[1004] = '\0'; + TEST_REJECTED_(text, EINVAL); +} + +static void TEST_environment_variable_strtoll_WHEN_ERASED() +{ + environment_variable_scope const present(VAR_NAME, "5"); + + TEST_READ_PARSED_(VAR_NAME, 5); + + environment_variable_scope const erased(VAR_NAME, ss_nullptr_k); + + TEST_READ_REJECTED_(VAR_NAME, EINVAL); +} + +static void TEST_environment_variable_strtoll_WHEN_VALUE_REPLACED() +{ + environment_variable_scope const first(VAR_NAME, "11"); + + TEST_READ_PARSED_(VAR_NAME, 11); + + environment_variable_scope const second(VAR_NAME, "22"); + + TEST_READ_PARSED_(VAR_NAME, 22); +} + +static void TEST_environment_variable_strtoll_EXACT_NAME() +{ + environment_variable_scope const extra_absent(VAR_NAME_EXTRA, ss_nullptr_k); + environment_variable_scope const prefix_absent(VAR_NAME_PREFIX, ss_nullptr_k); + environment_variable_scope const present(VAR_NAME, "42"); + + TEST_READ_PARSED_(VAR_NAME, 42); + TEST_READ_REJECTED_(VAR_NAME_EXTRA, EINVAL); + TEST_READ_REJECTED_(VAR_NAME_PREFIX, EINVAL); + + environment_variable_scope const erased(VAR_NAME, ss_nullptr_k); + environment_variable_scope const extra(VAR_NAME_EXTRA, "7"); + + TEST_READ_REJECTED_(VAR_NAME, EINVAL); + TEST_READ_PARSED_(VAR_NAME_EXTRA, 7); +} + +/* UNIX names are case-sensitive; the Windows CRT folds case. + */ +static void TEST_environment_variable_strtoll_CASE_SENSITIVITY() +{ + environment_variable_scope const lower(VAR_NAME_LOWER, ss_nullptr_k); + environment_variable_scope const upper(VAR_NAME_UPPER, "7"); + + TEST_READ_PARSED_(VAR_NAME_UPPER, 7); +#ifdef _WIN32 + + TEST_READ_PARSED_(VAR_NAME_LOWER, 7); +#else + + TEST_READ_REJECTED_(VAR_NAME_LOWER, EINVAL); +#endif +} + +static void TEST_environment_variable_strtoll_CPP_WRAPPER() +{ + environment_variable_scope const value(VAR_NAME, "9223372036854775807"); + stlsoft::ss_sint64_t result = SENTINEL_; + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_strtoll(VAR_NAME, &result)); + TEST_INT_EQ(STLSOFT_GEN_SINT64_SUFFIX(9223372036854775807), result); +} + +#ifndef _WIN32 + +/* An empty value is defined on UNIX, and it is not a number. The Windows + * CRT treats an empty assignment as erasure, so this case is UNIX-only. + */ +static void TEST_environment_variable_strtoll_WHEN_EMPTY() +{ + TEST_REJECTED_("", EINVAL); +} +#endif /* !_WIN32 */ +} // anonymous namespace + +#undef TEST_PARSED_ +#undef TEST_READ_PARSED_ +#undef TEST_READ_REJECTED_ +#undef TEST_REJECTED_ + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/CMakeLists.txt b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/CMakeLists.txt new file mode 100644 index 00000000..9107eda8 --- /dev/null +++ b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.component.stlsoft.system.environment_variable_exists entry.cpp) diff --git a/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/entry.cpp b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/entry.cpp new file mode 100644 index 00000000..7f7bbca0 --- /dev/null +++ b/test/component/stlsoft/system/test.component.stlsoft.system.environment_variable_exists/entry.cpp @@ -0,0 +1,259 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test.component.stlsoft.system.environment_variable_exists/entry.cpp + * + * Purpose: Component-tests for `stlsoft::environment_variable_exists()`. + * + * Created: 28th September 2026 + * Updated: 28th September 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +/* String-access shims must be visible before functions.hpp so the template + * overload can bind c_str_ptr() for these types. + */ +#include +#include +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include +#include + +/* Standard C++ header files */ +#include + +/* Standard C header files */ +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +namespace { + + static void TEST_environment_variable_exists_WHEN_ABSENT(); + static void TEST_environment_variable_exists_WHEN_PRESENT(); + static void TEST_environment_variable_exists_WHEN_ERASED(); + static void TEST_environment_variable_exists_WHEN_VALUE_REPLACED(); + static void TEST_environment_variable_exists_EXACT_NAME(); + static void TEST_environment_variable_exists_WITH_std_string(); + static void TEST_environment_variable_exists_WITH_simple_string(); +#ifndef _WIN32 + static void TEST_environment_variable_exists_WHEN_EMPTY(); +#endif /* !_WIN32 */ + static void TEST_environment_variable_exists_CASE_SENSITIVITY(); +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char* argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.component.stlsoft.system.environment_variable_exists", verbosity)) + { + XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_ABSENT); + XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_PRESENT); + XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_ERASED); + XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_VALUE_REPLACED); + XTESTS_RUN_CASE(TEST_environment_variable_exists_EXACT_NAME); + XTESTS_RUN_CASE(TEST_environment_variable_exists_WITH_std_string); + XTESTS_RUN_CASE(TEST_environment_variable_exists_WITH_simple_string); +#ifndef _WIN32 + XTESTS_RUN_CASE(TEST_environment_variable_exists_WHEN_EMPTY); +#endif /* !_WIN32 */ + XTESTS_RUN_CASE(TEST_environment_variable_exists_CASE_SENSITIVITY); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * constants + */ + +namespace { + + using platformstl::environment_variable_scope; + + char const VAR_NAME[] = "STLSOFT_CT_ENV_VAR_EXISTS"; + char const VAR_NAME_EXTRA[] = "STLSOFT_CT_ENV_VAR_EXISTS_EXTRA"; + char const VAR_NAME_PREFIX[] = "STLSOFT_CT_ENV_VAR"; + char const VAR_NAME_UPPER[] = "STLSOFT_CT_ENV_VAR_EXISTS_CASE"; + char const VAR_NAME_LOWER[] = "stlsoft_ct_env_var_exists_case"; +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +namespace { + +static void TEST_environment_variable_exists_WHEN_ABSENT() +{ + environment_variable_scope const absent(VAR_NAME, ss_nullptr_k); + + char const* const name = VAR_NAME; + + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name)); + TEST_BOOLEAN_FALSE(stlsoft::stlsoft_C_environment_variable_exists_m(name)); + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists("STLSOFT_CT_ENV_VAR_EXISTS_NO_SUCH")); +} + +static void TEST_environment_variable_exists_WHEN_PRESENT() +{ + environment_variable_scope const present(VAR_NAME, "present"); + + char const* const name = VAR_NAME; + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); + TEST_BOOLEAN_TRUE(stlsoft::stlsoft_C_environment_variable_exists_m(name)); +} + +static void TEST_environment_variable_exists_WHEN_ERASED() +{ + environment_variable_scope const present(VAR_NAME, "present"); + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME)); + + environment_variable_scope const erased(VAR_NAME, ss_nullptr_k); + + char const* const name = VAR_NAME; + + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name)); +} + +static void TEST_environment_variable_exists_WHEN_VALUE_REPLACED() +{ + environment_variable_scope const first(VAR_NAME, "one"); + + char const* const name = VAR_NAME; + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); + + environment_variable_scope const second(VAR_NAME, "two"); + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); +} + +static void TEST_environment_variable_exists_EXACT_NAME() +{ + environment_variable_scope const extra_absent(VAR_NAME_EXTRA, ss_nullptr_k); + environment_variable_scope const prefix_absent(VAR_NAME_PREFIX, ss_nullptr_k); + environment_variable_scope const present(VAR_NAME, "value"); + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME)); + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME_EXTRA)); + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME_PREFIX)); + + environment_variable_scope const erased(VAR_NAME, ss_nullptr_k); + environment_variable_scope const extra(VAR_NAME_EXTRA, "extra"); + + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME)); + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME_EXTRA)); +} + +static void TEST_environment_variable_exists_WITH_std_string() +{ + { + environment_variable_scope const absent(VAR_NAME, ss_nullptr_k); + + std::string const name(VAR_NAME); + + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name)); + } + + environment_variable_scope const present(VAR_NAME, "present"); + + std::string const name(VAR_NAME); + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); +} + +static void TEST_environment_variable_exists_WITH_simple_string() +{ + { + environment_variable_scope const absent(VAR_NAME, ss_nullptr_k); + + stlsoft::simple_string const name(VAR_NAME); + + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(name)); + } + + environment_variable_scope const present(VAR_NAME, "present"); + + stlsoft::simple_string const name(VAR_NAME); + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); +} +#ifndef _WIN32 + +/* An empty value is a defined variable on UNIX. The Windows CRT treats an + * empty assignment as erasure, so this case is UNIX-only. + */ +static void TEST_environment_variable_exists_WHEN_EMPTY() +{ + environment_variable_scope const empty(VAR_NAME, ""); + + char const* const value = ::getenv(VAR_NAME); + + XTESTS_REQUIRE(TEST_PTR_NE(NULL, value)); + TEST_MS_EQ("", value); + + char const* const name = VAR_NAME; + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(name)); +} +#endif /* !_WIN32 */ + +/* UNIX names are case-sensitive; the Windows CRT folds case. + */ +static void TEST_environment_variable_exists_CASE_SENSITIVITY() +{ + environment_variable_scope const lower(VAR_NAME_LOWER, ss_nullptr_k); + environment_variable_scope const upper(VAR_NAME_UPPER, "1"); + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME_UPPER)); +#ifdef _WIN32 + + TEST_BOOLEAN_TRUE(stlsoft::environment_variable_exists(VAR_NAME_LOWER)); +#else + + TEST_BOOLEAN_FALSE(stlsoft::environment_variable_exists(VAR_NAME_LOWER)); +#endif +} +} // anonymous namespace + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/component/unixstl/filesystem/CMakeLists.txt b/test/component/unixstl/filesystem/CMakeLists.txt index 7d8d9318..be579252 100644 --- a/test/component/unixstl/filesystem/CMakeLists.txt +++ b/test/component/unixstl/filesystem/CMakeLists.txt @@ -1,4 +1,5 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(filesystem_traits) add_subdirectory(glob_sequence) add_subdirectory(memory_mapped_file) add_subdirectory(readdir_sequence) diff --git a/test/component/unixstl/filesystem/filesystem_traits/CMakeLists.txt b/test/component/unixstl/filesystem/filesystem_traits/CMakeLists.txt new file mode 100644 index 00000000..8460ebf3 --- /dev/null +++ b/test/component/unixstl/filesystem/filesystem_traits/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(opendir) diff --git a/test/component/unixstl/filesystem/filesystem_traits/opendir/CMakeLists.txt b/test/component/unixstl/filesystem/filesystem_traits/opendir/CMakeLists.txt new file mode 100644 index 00000000..204e5114 --- /dev/null +++ b/test/component/unixstl/filesystem/filesystem_traits/opendir/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.component.unixstl.filesystem.filesystem_traits.opendir entry.cpp) diff --git a/test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp b/test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp new file mode 100644 index 00000000..991257a2 --- /dev/null +++ b/test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp @@ -0,0 +1,449 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: test/component/unixstl/filesystem/filesystem_traits/opendir/entry.cpp) + * + * Purpose: Component-tests for `unixstl::filesystem_traits`, in respect of + * the directory-enumeration functions `open_dir()`, `read_dir()`, + * `rewind_dir()`, and `close_dir()`. + * + * Created: 8th October 2026 + * Updated: 8th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +#include + +/* xTests header files */ +#include +#include +#include +#include + +/* Standard C++ header files */ +#include +#include +#include + +/* Standard C header files */ +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +namespace { + + static void TEST_open_dir_NONEXISTENT_directory(); + static void TEST_read_dir_EMPTY_directory(); + static void TEST_read_dir_NON_EMPTY_directory(); + static void TEST_rewind_dir_AFTER_EXHAUSTION(); + static void TEST_rewind_dir_AFTER_PARTIAL_READ(); + static void TEST_rewind_dir_BEFORE_ANY_READ(); + static void TEST_rewind_dir_EMPTY_directory(); + static void TEST_rewind_dir_MULTIPLE_times(); +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char *argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.component.unixstl.filesystem.filesystem_traits.opendir", verbosity)) + { + XTESTS_RUN_CASE(TEST_open_dir_NONEXISTENT_directory); + XTESTS_RUN_CASE(TEST_read_dir_EMPTY_directory); + XTESTS_RUN_CASE(TEST_read_dir_NON_EMPTY_directory); + XTESTS_RUN_CASE(TEST_rewind_dir_AFTER_EXHAUSTION); + XTESTS_RUN_CASE(TEST_rewind_dir_AFTER_PARTIAL_READ); + XTESTS_RUN_CASE(TEST_rewind_dir_BEFORE_ANY_READ); + XTESTS_RUN_CASE(TEST_rewind_dir_EMPTY_directory); + XTESTS_RUN_CASE(TEST_rewind_dir_MULTIPLE_times); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * types + */ + +namespace { + + typedef unixstl::filesystem_traits fs_traits_t; + typedef platformstl::path path_t; + typedef std::vector names_t; + using ::xtests::cpp::util::temp_directory; + using ::xtests::cpp::util::temp_file; +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * helper functions + */ + +namespace { + + /* Three temporary files, created in a given (temporary) directory, + * and deleted on destruction. Must be declared after, so destroyed + * before, the `temp_directory` in which they are created. + */ + class three_files + { + public: + explicit + three_files( + char const* dir + ) + : m_f0(temp_file::DeleteOnClose | temp_file::CloseOnOpen, dir) + , m_f1(temp_file::DeleteOnClose | temp_file::CloseOnOpen, dir) + , m_f2(temp_file::DeleteOnClose | temp_file::CloseOnOpen, dir) + {} + + public: + /* The names (i.e. without directory) of the files, sorted */ + names_t + names() const + { + names_t result; + + result.push_back(leaf_name(m_f0.c_str())); + result.push_back(leaf_name(m_f1.c_str())); + result.push_back(leaf_name(m_f2.c_str())); + + std::sort(result.begin(), result.end()); + + return result; + } + + private: + static + std::string + leaf_name( + char const* path + ) + { + char const* const sep = ::strrchr(path, '/'); + + return (NULL == sep) ? path : (sep + 1); + } + + private: + temp_file m_f0; + temp_file m_f1; + temp_file m_f2; + }; + + /* Reads up to `max` entries from `h` into `names`; returns the number + * of entries read + */ + static + size_t + read_entries( + DIR* h + , names_t& names + , size_t max + ) + { + size_t n = 0; + + for (struct dirent const* de; n < max && NULL != (de = fs_traits_t::read_dir(h)); ++n) + { + names.push_back(de->d_name); + } + + return n; + } + + /* Reads all remaining entries from `h` into `names`; returns the number + * of entries read + */ + static + size_t + read_all_entries( + DIR* h + , names_t& names + ) + { + return read_entries(h, names, ~size_t(0)); + } + + /* Removes the entries "." and ".." and sorts, so that sequences can be + * compared irrespective of the (unspecified) order of enumeration + */ + static + names_t + normalise( + names_t names + ) + { + names_t result; + + for (names_t::const_iterator i = names.begin(); names.end() != i; ++i) + { + if ("." != *i && ".." != *i) + { + result.push_back(*i); + } + } + + std::sort(result.begin(), result.end()); + + return result; + } + +} // anonymous namespace + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +namespace { + +static void TEST_open_dir_NONEXISTENT_directory() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + path_t const path = path_t(td.c_str()) / "does-not-exist"; + + DIR* const h = fs_traits_t::open_dir(path.c_str()); + + TEST_BOOLEAN_TRUE(NULL == h); + + if (NULL != h) + { + fs_traits_t::close_dir(h); + } +} + +static void TEST_read_dir_EMPTY_directory() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + + DIR* const h = fs_traits_t::open_dir(td.c_str()); + + if (NULL == h) + { + TEST_FAIL("failed to open directory"); + } + else + { + names_t names; + + read_all_entries(h, names); + + TEST_INT_EQ(0u, normalise(names).size()); + + TEST_BOOLEAN_TRUE(NULL == fs_traits_t::read_dir(h)); + + fs_traits_t::close_dir(h); + } +} + +static void TEST_read_dir_NON_EMPTY_directory() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + + three_files files(td.c_str()); + + DIR* const h = fs_traits_t::open_dir(td.c_str()); + + if (NULL == h) + { + TEST_FAIL("failed to open directory"); + } + else + { + names_t names; + + read_all_entries(h, names); + + TEST_BOOLEAN_TRUE(files.names() == normalise(names)); + + fs_traits_t::close_dir(h); + } +} + +static void TEST_rewind_dir_BEFORE_ANY_READ() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + + three_files files(td.c_str()); + + DIR* const h = fs_traits_t::open_dir(td.c_str()); + + if (NULL == h) + { + TEST_FAIL("failed to open directory"); + } + else + { + names_t names; + + fs_traits_t::rewind_dir(h); + + read_all_entries(h, names); + + TEST_BOOLEAN_TRUE(files.names() == normalise(names)); + + fs_traits_t::close_dir(h); + } +} + +static void TEST_rewind_dir_AFTER_PARTIAL_READ() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + + three_files files(td.c_str()); + + DIR* const h = fs_traits_t::open_dir(td.c_str()); + + if (NULL == h) + { + TEST_FAIL("failed to open directory"); + } + else + { + names_t first_pass; + names_t second_pass; + + // Read the first two entries (of "." + ".." + 3 files, in some order) + + TEST_INT_EQ(2u, read_entries(h, first_pass, 2)); + + fs_traits_t::rewind_dir(h); + + read_all_entries(h, second_pass); + + // After the rewind, *all* entries are returned, including those + // that had been read before the rewind + + TEST_INT_EQ(5u, second_pass.size()); + TEST_BOOLEAN_TRUE(files.names() == normalise(second_pass)); + + fs_traits_t::close_dir(h); + } +} + +static void TEST_rewind_dir_AFTER_EXHAUSTION() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + + three_files files(td.c_str()); + + DIR* const h = fs_traits_t::open_dir(td.c_str()); + + if (NULL == h) + { + TEST_FAIL("failed to open directory"); + } + else + { + names_t first_pass; + names_t second_pass; + + read_all_entries(h, first_pass); + + TEST_BOOLEAN_TRUE(NULL == fs_traits_t::read_dir(h)); + + fs_traits_t::rewind_dir(h); + + read_all_entries(h, second_pass); + + TEST_BOOLEAN_TRUE(files.names() == normalise(first_pass)); + TEST_BOOLEAN_TRUE(files.names() == normalise(second_pass)); + TEST_INT_EQ(first_pass.size(), second_pass.size()); + + fs_traits_t::close_dir(h); + } +} + +static void TEST_rewind_dir_EMPTY_directory() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + + DIR* const h = fs_traits_t::open_dir(td.c_str()); + + if (NULL == h) + { + TEST_FAIL("failed to open directory"); + } + else + { + names_t names; + + read_all_entries(h, names); + + fs_traits_t::rewind_dir(h); + + names_t names_after; + + read_all_entries(h, names_after); + + TEST_INT_EQ(0u, normalise(names_after).size()); + TEST_INT_EQ(names.size(), names_after.size()); + + fs_traits_t::close_dir(h); + } +} + +static void TEST_rewind_dir_MULTIPLE_times() +{ + temp_directory td(temp_directory::EmptyOnOpen | temp_directory::EmptyOnClose | temp_directory::RemoveOnClose); + + three_files files(td.c_str()); + + DIR* const h = fs_traits_t::open_dir(td.c_str()); + + if (NULL == h) + { + TEST_FAIL("failed to open directory"); + } + else + { + for (int pass = 0; pass != 3; ++pass) + { + names_t names; + + read_all_entries(h, names); + + TEST_BOOLEAN_TRUE(files.names() == normalise(names)); + + fs_traits_t::rewind_dir(h); + } + + fs_traits_t::close_dir(h); + } +} +} // anonymous namespace + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/scratch/unixstl/CMakeLists.txt b/test/scratch/unixstl/CMakeLists.txt index beef2694..719e8486 100644 --- a/test/scratch/unixstl/CMakeLists.txt +++ b/test/scratch/unixstl/CMakeLists.txt @@ -1,3 +1,4 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(filesystem) add_subdirectory(synch) add_subdirectory(system) diff --git a/test/scratch/unixstl/filesystem/CMakeLists.txt b/test/scratch/unixstl/filesystem/CMakeLists.txt new file mode 100644 index 00000000..5fa1138e --- /dev/null +++ b/test/scratch/unixstl/filesystem/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(rewind_dir) diff --git a/test/scratch/unixstl/filesystem/rewind_dir/CMakeLists.txt b/test/scratch/unixstl/filesystem/rewind_dir/CMakeLists.txt new file mode 100644 index 00000000..586b2901 --- /dev/null +++ b/test/scratch/unixstl/filesystem/rewind_dir/CMakeLists.txt @@ -0,0 +1,2 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_example_program(test.scratch.unixstl.filesystem.rewind_dir main.cpp) diff --git a/test/scratch/unixstl/filesystem/rewind_dir/main.cpp b/test/scratch/unixstl/filesystem/rewind_dir/main.cpp new file mode 100644 index 00000000..595999e0 --- /dev/null +++ b/test/scratch/unixstl/filesystem/rewind_dir/main.cpp @@ -0,0 +1,112 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: rewind_dir/main.cpp + * + * Purpose: Scratch test for `unixstl::filesystem_traits<>::rewind_dir()`, + * exploring its interaction with `open_dir()`, `read_dir()`, and + * `close_dir()`, on a directory given on the command-line (or the + * current directory, if none is given). + * + * Created: 8th October 2026 + * Updated: 8th October 2026 + * + * ////////////////////////////////////////////////////////////////////// */ + + +#include + +#include + +#include +#include + + +typedef unixstl::filesystem_traits fs_traits_t; + + +namespace { + + /* Reads up to `max` entries from `h`, printing each, and returns the + * number read + */ + size_t + dump_entries( + DIR* h + , char const* label + , size_t max + ) + { + size_t n = 0; + + fprintf(stdout, "%s:\n", label); + + for (struct dirent const* de; n < max && NULL != (de = fs_traits_t::read_dir(h)); ++n) + { + fprintf(stdout, "\t%s\n", de->d_name); + } + + fprintf(stdout, "\t(%lu entries)\n", static_cast(n)); + + return n; + } +} // anonymous namespace + + +int main(int argc, char* argv[]) +{ + char const* const program_name = platformstl::get_executable_name_from_path(argv[0]).ptr; + char const* dir = "."; + + if (argc > 1) + { + dir = argv[1]; + } + + DIR* const h = fs_traits_t::open_dir(dir); + + if (NULL == h) + { + fprintf(stderr, "%s: could not open directory '%s'\n", program_name, dir); + + return EXIT_FAILURE; + } + else + { + size_t const n1 = dump_entries(h, "first pass (full)", ~size_t(0)); + size_t const n2 = dump_entries(h, "(no rewind; expect nothing)", ~size_t(0)); + + fs_traits_t::rewind_dir(h); + + size_t const n3 = dump_entries(h, "second pass (after rewind)", ~size_t(0)); + + fs_traits_t::rewind_dir(h); + + size_t const n4 = dump_entries(h, "third pass (after rewind; first two entries only)", 2); + + fs_traits_t::rewind_dir(h); + + size_t const n5 = dump_entries(h, "fourth pass (after rewind from partial read)", ~size_t(0)); + + fs_traits_t::close_dir(h); + + fprintf(stdout, "\nsummary: n1=%lu; n2=%lu; n3=%lu; n4=%lu; n5=%lu\n" + , static_cast(n1) + , static_cast(n2) + , static_cast(n3) + , static_cast(n4) + , static_cast(n5) + ); + + if (0 != n2 || n1 != n3 || n1 != n5) + { + fprintf(stderr, "%s: unexpected entry counts\n", program_name); + + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/unit/platformstl/CMakeLists.txt b/test/unit/platformstl/CMakeLists.txt index 19138ddd..985b6996 100644 --- a/test/unit/platformstl/CMakeLists.txt +++ b/test/unit/platformstl/CMakeLists.txt @@ -1,2 +1,3 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(process) add_subdirectory(system) diff --git a/test/unit/platformstl/process/CMakeLists.txt b/test/unit/platformstl/process/CMakeLists.txt new file mode 100644 index 00000000..992517e9 --- /dev/null +++ b/test/unit/platformstl/process/CMakeLists.txt @@ -0,0 +1,3 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(functions) + diff --git a/test/unit/platformstl/process/functions/CMakeLists.txt b/test/unit/platformstl/process/functions/CMakeLists.txt new file mode 100644 index 00000000..bd138b53 --- /dev/null +++ b/test/unit/platformstl/process/functions/CMakeLists.txt @@ -0,0 +1,3 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.unit.platformstl.process.functions entry.c) + diff --git a/test/unit/platformstl/process/functions/entry.c b/test/unit/platformstl/process/functions/entry.c new file mode 100644 index 00000000..0133a8ab --- /dev/null +++ b/test/unit/platformstl/process/functions/entry.c @@ -0,0 +1,106 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: functions/entry.c + * + * Purpose: Unit-tests for `platformstl_C_get_current_process_id()`. + * + * Created: 8th October 2026 + * Updated: 8th October 2026 + * + * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include + +/* Standard C header files */ +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +static void TEST_EQUALS_SELECTED_PLATFORM_FUNCTION(void); +static void TEST_IS_POSITIVE(void); + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char *argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.platformstl.process.functions", verbosity)) + { + XTESTS_RUN_CASE(TEST_EQUALS_SELECTED_PLATFORM_FUNCTION); + XTESTS_RUN_CASE(TEST_IS_POSITIVE); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +static void TEST_EQUALS_SELECTED_PLATFORM_FUNCTION(void) +{ + int expected; + int actual; + +#if defined(PLATFORMSTL_OS_IS_UNIX) + + expected = unixstl_C_get_current_process_id(); +#elif defined(PLATFORMSTL_OS_IS_WINDOWS) + + expected = winstl_C_get_current_process_id(); +#else + +# error Platform not discriminated +#endif + + actual = platformstl_C_get_current_process_id(); + + TEST_INT_EQ(expected, actual); +} + +static void TEST_IS_POSITIVE(void) +{ + int const result = platformstl_C_get_current_process_id(); + + TEST_INT_GT(0, result); +} + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/unit/unixstl/CMakeLists.txt b/test/unit/unixstl/CMakeLists.txt index 5eb6b8bd..ba184416 100644 --- a/test/unit/unixstl/CMakeLists.txt +++ b/test/unit/unixstl/CMakeLists.txt @@ -1,3 +1,4 @@ # SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten add_subdirectory(filesystem) +add_subdirectory(process) add_subdirectory(shims) diff --git a/test/unit/unixstl/process/CMakeLists.txt b/test/unit/unixstl/process/CMakeLists.txt new file mode 100644 index 00000000..992517e9 --- /dev/null +++ b/test/unit/unixstl/process/CMakeLists.txt @@ -0,0 +1,3 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(functions) + diff --git a/test/unit/unixstl/process/functions/CMakeLists.txt b/test/unit/unixstl/process/functions/CMakeLists.txt new file mode 100644 index 00000000..6884e0ba --- /dev/null +++ b/test/unit/unixstl/process/functions/CMakeLists.txt @@ -0,0 +1,3 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.unit.unixstl.process.functions entry.c) + diff --git a/test/unit/unixstl/process/functions/entry.c b/test/unit/unixstl/process/functions/entry.c new file mode 100644 index 00000000..e3b090c5 --- /dev/null +++ b/test/unit/unixstl/process/functions/entry.c @@ -0,0 +1,127 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: functions/entry.c + * + * Purpose: Unit-tests for `unixstl_C_get_current_process_id()`. + * + * Created: 8th October 2026 + * Updated: 8th October 2026 + * + * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include + +/* Standard C header files */ +#include + +#if defined(_WIN32) && \ + ( defined(STLSOFT_COMPILER_IS_MSVC) || \ + defined(STLSOFT_COMPILER_IS_INTEL)) +# include +#else +# include +# include +#endif + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +static void TEST_EQUALS_NATIVE_PROCESS_ID(void); +static void TEST_IS_POSITIVE(void); +static void TEST_IS_STABLE(void); + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char *argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.unixstl.process.functions", verbosity)) + { + XTESTS_RUN_CASE(TEST_EQUALS_NATIVE_PROCESS_ID); + XTESTS_RUN_CASE(TEST_IS_POSITIVE); + XTESTS_RUN_CASE(TEST_IS_STABLE); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +static void TEST_EQUALS_NATIVE_PROCESS_ID(void) +{ + int expected; + int actual; + +#if 0 +#elif defined(_WIN32) && \ + ( 0 ||\ + defined(STLSOFT_COMPILER_IS_MSVC) ||\ + defined(STLSOFT_COMPILER_IS_INTEL) ||\ + 0) + + expected = _getpid(); +#else + + expected = STLSOFT_STATIC_CAST(int, getpid()); +#endif + + actual = unixstl_C_get_current_process_id(); + + TEST_INT_EQ(expected, actual); +} + +static void TEST_IS_POSITIVE(void) +{ + int const result = unixstl_C_get_current_process_id(); + + TEST_INT_GT(0, result); +} + +static void TEST_IS_STABLE(void) +{ + int const first = unixstl_C_get_current_process_id(); + int const second = unixstl_C_get_current_process_id(); + + TEST_INT_EQ(first, second); +} + + +/* ///////////////////////////// end of file //////////////////////////// */ + diff --git a/test/unit/versions/entry.cpp b/test/unit/versions/entry.cpp index b18bd6a3..4f1128d1 100644 --- a/test/unit/versions/entry.cpp +++ b/test/unit/versions/entry.cpp @@ -4,7 +4,7 @@ * Purpose: Unit-tests for versions * * Created: 23rd August 2025 - * Updated: 30th September 2026 + * Updated: 10th October 2026 * * ////////////////////////////////////////////////////////////////////// */ @@ -133,7 +133,7 @@ namespace { static void TEST__STLSOFT_VER() { - TEST_INT_EQ(_STLSOFT_VER_1_11_1_RC7, _STLSOFT_VER); + TEST_INT_EQ(_STLSOFT_VER_1_11_1_RC8, _STLSOFT_VER); } #ifdef STLSOFT_HAS_ACE @@ -152,7 +152,7 @@ static void TEST__ATLSTL_VER() static void TEST__PLATFORMSTL_VER() { - TEST_INT_EQ(_PLATFORMSTL_VER_1_10_0_B01, _PLATFORMSTL_VER); + TEST_INT_EQ(_PLATFORMSTL_VER_1_10_0_B02, _PLATFORMSTL_VER); } #if 0 @@ -160,7 +160,7 @@ static void TEST__PLATFORMSTL_VER() static void TEST__UNIXSTL_VER() { - TEST_INT_EQ(_UNIXSTL_VER_1_8_6_B06, _UNIXSTL_VER); + TEST_INT_EQ(_UNIXSTL_VER_1_8_6_B07, _UNIXSTL_VER); } #elif defined(PLATFORMSTL_OS_IS_WINDOWS) @@ -171,7 +171,7 @@ static void TEST__COMSTL_VER() static void TEST__WINSTL_VER() { - TEST_INT_EQ(_WINSTL_VER_1_13_0_B03, _WINSTL_VER); + TEST_INT_EQ(_WINSTL_VER_1_13_0_B04, _WINSTL_VER); } #endif #ifdef STLSOFT_HAS_MFC diff --git a/test/unit/winstl/CMakeLists.txt b/test/unit/winstl/CMakeLists.txt index 668b342f..64aff324 100644 --- a/test/unit/winstl/CMakeLists.txt +++ b/test/unit/winstl/CMakeLists.txt @@ -4,5 +4,6 @@ add_subdirectory(conversion) add_subdirectory(diagnostics) add_subdirectory(filesystem) add_subdirectory(memory) +add_subdirectory(process) add_subdirectory(system) add_subdirectory(util) diff --git a/test/unit/winstl/process/CMakeLists.txt b/test/unit/winstl/process/CMakeLists.txt new file mode 100644 index 00000000..992517e9 --- /dev/null +++ b/test/unit/winstl/process/CMakeLists.txt @@ -0,0 +1,3 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +add_subdirectory(functions) + diff --git a/test/unit/winstl/process/functions/CMakeLists.txt b/test/unit/winstl/process/functions/CMakeLists.txt new file mode 100644 index 00000000..f7bbb016 --- /dev/null +++ b/test/unit/winstl/process/functions/CMakeLists.txt @@ -0,0 +1,3 @@ +# SIS:AUTO_GENERATED: Remove this line if you edit the file, otherwise it will be overwritten +define_automated_test_program(test.unit.winstl.process.functions entry.c) + diff --git a/test/unit/winstl/process/functions/entry.c b/test/unit/winstl/process/functions/entry.c new file mode 100644 index 00000000..4f4cdb37 --- /dev/null +++ b/test/unit/winstl/process/functions/entry.c @@ -0,0 +1,116 @@ +/* ///////////////////////////////////////////////////////////////////////// + * File: functions/entry.c + * + * Purpose: Unit-tests for `winstl_C_get_current_process_id()`. + * + * Created: 8th October 2026 + * Updated: 8th October 2026 + * + * Copyright (c) 2026, Matthew Wilson and Synesis Information Systems + * + * ////////////////////////////////////////////////////////////////////// */ + + +/* ///////////////////////////////////////////////////////////////////////// + * includes + */ + +/* ///////////////////////////////////// + * test component header file include(s) + */ + +#include + +/* ///////////////////////////////////// + * general includes + */ + +/* xTests header files */ +#include +#include + +/* STLSoft header files */ +#include + +/* Standard C header files */ +#include + +#include +#include + + +/* ///////////////////////////////////////////////////////////////////////// + * forward declarations + */ + +static void TEST_EQUALS_GETPID(void); +static void TEST_EQUALS_CURRENT_PROCESS_ID(void); +static void TEST_IS_POSITIVE(void); +static void TEST_IS_STABLE(void); + + +/* ///////////////////////////////////////////////////////////////////////// + * main() + */ + +int main(int argc, char *argv[]) +{ + int retCode = EXIT_SUCCESS; + int verbosity = 2; + + XTESTS_COMMANDLINE_PARSEVERBOSITY(argc, argv, &verbosity); + + if (XTESTS_START_RUNNER("test.unit.winstl.process.functions", verbosity)) + { + XTESTS_RUN_CASE(TEST_EQUALS_GETPID); + XTESTS_RUN_CASE(TEST_EQUALS_CURRENT_PROCESS_ID); + XTESTS_RUN_CASE(TEST_IS_POSITIVE); + XTESTS_RUN_CASE(TEST_IS_STABLE); + + XTESTS_PRINT_RESULTS(); + + XTESTS_END_RUNNER_UPDATE_EXITCODE(&retCode); + } + + return retCode; +} + + +/* ///////////////////////////////////////////////////////////////////////// + * test function implementations + */ + +static void TEST_EQUALS_GETPID(void) +{ + int const expected = _getpid(); + int const actual = winstl_C_get_current_process_id(); + + TEST_INT_EQ(expected, actual); +} + +static void TEST_EQUALS_CURRENT_PROCESS_ID(void) +{ + int const expected = STLSOFT_STATIC_CAST(int, GetCurrentProcessId()); + int const actual = winstl_C_get_current_process_id(); + + TEST_INT_EQ(expected, actual); +} + +static void TEST_IS_POSITIVE(void) +{ + int const result = winstl_C_get_current_process_id(); + + TEST_INT_GT(0, result); +} + +static void TEST_IS_STABLE(void) +{ + int const first = winstl_C_get_current_process_id(); + int const second = winstl_C_get_current_process_id(); + + TEST_INT_EQ(first, second); +} + + +/* ///////////////////////////// end of file //////////////////////////// */ +