From 682a4c95048533a7fd6315fd788ae6a9008e91a9 Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Wed, 17 Jun 2026 14:50:45 -0600 Subject: [PATCH 01/10] Sym link CLAUDE.md to AGENTS.md Claude seems to want it - port of open-mpi/ompi#14065 Signed-off-by: Ralph Castain (cherry picked from commit 4a2e55ea6edc4390e15b74058c624c3b93329224) --- CLAUDE.md | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..9abb2cc2ed --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,396 @@ +# AGENTS.md — Orientation for AI Coding Agents and Human Contributors + +This document is an orientation guide for AI coding agents and their human operators working in the PRRTE code base. +This file is an *orientation map*, not the +full rulebook: the authoritative, human-maintained documentation lives +under [`docs/developers/`](docs/developers/) and +[`docs/contributing.rst`](docs/contributing.rst) (rendered at +). When this file and those docs disagree, +**the docs win** — and please fix this file. + +AI-assisted contributions are welcome. But PRRTE runs on the largest +supercomputers in the world and across a huge range of operating +systems and hardware. It is also used by numerous research groups +looking at developments such as elastic environments. We want careful, +portable code — not plausible-looking code that solves one problem in +one environment or use-case at the expense of others. Hold yourself to +the same bar as a thoughtful human contributor. + +--- + +## What is PRRTE? + +PRRTE (PMIx Reference RunTime Environment) is an open-source, production +runtime system for launching and managing parallel jobs in HPC environments. +Unlike a library, PRRTE exposes no public API for callers — it is a +collection of executable tools that users invoke directly. PRRTE acts as a +reference implementation of the PMIx-defined runtime services, and it relies +heavily on the PMIx project for its internal infrastructure: MCA +(Modular Component Architecture), utility routines, data structures, and +threading primitives. Many PMIx symbols and headers PRRTE uses are internal +PMIx interfaces, **not** the PMIx public library API. + +Source repository: https://github.com/openpmix/prrte +Documentation: https://docs.prrte.org/ + +--- + +## Terminology + +Understanding PRRTE's vocabulary is essential before reading or modifying +code. + +| Term | Meaning | +|------|---------| +| **DVM** | Distributed Virtual Machine — a persistent set of PRRTE daemons spread across an allocation, ready to launch jobs on demand. | +| **HNP** | Head Node Process — the `prte` process that acts as the DVM controller. Also called the DVM master. | +| **prted** | PRRTE daemon — one instance runs on each node in the DVM and manages local processes. | +| **job** | A set of processes launched together under a single `prun` invocation. Each job has a unique namespace. | +| **namespace** | A string that uniquely identifies a job within a DVM session (inherited from PMIx). | +| **rank** | A process's integer identifier within its namespace (global rank), within its node (node rank), or within the local set of processes on a node (local rank). | +| **app context** | A description of one executable to run: path, argv, environment, and process count. A single `prun` may specify multiple app contexts. | +| **session** | The lifetime of a DVM — from `prte` startup through `pterm` shutdown. | +| **schizo** | The "personality" framework. Schizo components translate between PRRTE's internal model and the command-line conventions of specific launchers (e.g., Open MPI's `mpirun`, SLURM's `srun`). | +| **MCA** | Modular Component Architecture — the plugin system, inherited from PMIx, that PRRTE uses for every swappable subsystem. | +| **framework** | An MCA abstraction layer that defines the interface for one functional area (e.g., `plm`, `rmaps`). | +| **component** | A plugin that implements a framework's interface for a specific environment or algorithm (e.g., `plm/slurm`). | +| **module** | The active instance of a component, returned after it wins selection. | + +--- + +## User-Facing Tools + +PRRTE provides no linkable library. All user interaction is through +executables. + +| Tool | Source | Purpose | +|------|--------|---------| +| `prte` | `src/tools/prte/` | Start a DVM (the HNP process). Allocates and connects daemons across an allocation. | +| `prun` | `src/tools/prun/` | Submit and manage a job within a running DVM. The everyday launch command. | +| `pterm` | `src/tools/pterm/` | Terminate a running DVM cleanly. | +| `prted` | `src/tools/prted/` | The per-node daemon, normally launched by the HNP — not invoked directly by users. | +| `prte_info` | `src/tools/prte_info/` | Query build configuration, list available MCA components, and display version information. | +| `pcc` | `src/tools/pcc/` | Compiler wrapper for applications that directly embed PMIx (not PRRTE — PRRTE has no linkable library). | + +--- + +## Source Layout + +``` +src/ + tools/ # Executable entry points (prte, prun, pterm, prted, prte_info, pcc) + mca/ # All MCA frameworks and their components + runtime/ # Global state, init/finalize, job/node/proc data structures + rml/ # Runtime Messaging Layer (point-to-point communication between daemons) + util/ # Internal utilities (hostfile parsing, name formatting, attributes, ...) + include/ # Internal header files (types.h, constants.h, ...) + pmix/ # Thin shim connecting PRRTE to its PMIx dependency + event/ # Libevent integration + hwloc/ # hwloc integration (topology, binding) +``` + +The three central data structures — `prte_job_t`, `prte_node_t`, and +`prte_proc_t` — are defined in `src/runtime/prte_globals.h` and carry +all runtime state for a running job. Code throughout the tree reaches +for these; understand them before touching launch, mapping, or error +handling paths. + +--- + +## MCA Frameworks + +Every swappable PRRTE subsystem is an MCA framework. The framework +directory contains a `base/` subdirectory (selection, default behaviors, +utility stubs) and one or more component subdirectories. + +| Framework | Location | Responsibility | +|-----------|----------|----------------| +| `ess` | `src/mca/ess/` | Environment-Specific Services — init/finalize for each process role (HNP, daemon, tool). | +| `plm` | `src/mca/plm/` | Process Launch Manager — spawns prted daemons across the system. Components: `ssh`, `slurm`, `lsf`, `pals`. | +| `ras` | `src/mca/ras/` | Resource Allocation Subsystem — discovers nodes and slots. Components: `slurm`, `pbs`, `lsf`, `flux`, `gridengine`, `hosts`. | +| `rmaps` | `src/mca/rmaps/` | Resource Mapping — assigns processes to nodes/slots. Components: `round_robin`, `ppr`, `rank_file`, `seq`. | +| `odls` | `src/mca/odls/` | PRRTE Daemon Local Launch Subsystem — the per-node daemon (prted) forks/execs application processes. | +| `iof` | `src/mca/iof/` | I/O Forwarding — routes stdout/stderr/stdin between daemons and the HNP. | +| `grpcomm` | `src/mca/grpcomm/` | Group Communication — collective operations among daemons (broadcast, barrier). | +| `errmgr` | `src/mca/errmgr/` | Error Manager — handles process faults, abnormal exits, and propagation of errors. | +| `state` | `src/mca/state/` | State Machine — drives the DVM and job lifecycle through defined states/transitions. | +| `schizo` | `src/mca/schizo/` | Personality layer — parses CLI options and environment for specific launcher personalities (prte, ompi). | +| `filem` | `src/mca/filem/` | File Management — pre-positions files across nodes before launch. | +| `prtereachable` | `src/mca/prtereachable/` | Reachability — determines which network interfaces can reach each node. | +| `prtebacktrace` | `src/mca/prtebacktrace/` | Backtrace support for crash diagnostics. | +| `prtedl` | `src/mca/prtedl/` | Dynamic linker abstraction (dlopen/dlclose). | +| `prteinstalldirs` | `src/mca/prteinstalldirs/` | Installation directory queries. | + +### MCA component structure + +Each component directory must contain: +- `.h` — component struct (`prte___component_t`) +- `.c` — component registration, open, query, close +- `_module.c` (or similar) — module implementation +- `Makefile.am` + +The component's `query` function returns a priority; the highest-priority +component that successfully opens wins selection. + +--- + +## PRRTE's Relationship with PMIx + +PRRTE depends on PMIx (minimum version `6.1.0`) and uses PMIx internals +extensively. This is not the same as calling the PMIx public library API. + +**What PRRTE takes from PMIx:** + +- **MCA infrastructure** (`src/mca/base/`, `src/mca/mca.h`) — the entire + plugin/component system is PMIx's code, not PRRTE's. +- **Data structures and classes** — `pmix_list_t`, `pmix_pointer_array_t`, + `pmix_hash_table_t`, `pmix_ring_buffer_t`, `pmix_value_array_t`. +- **Threading primitives** — `pmix_mutex_t`, `pmix_condition_t`, + `pmix_threads_*`. +- **Utility functions** — `pmix_argv_*`, `pmix_environ_*`, + `pmix_output_*`, `pmix_cmd_line_*`. +- **Event loop** — PMIx's libevent integration and progress threads. +- **Data packing/unpacking** — PMIx's buffer and bfrops subsystem. + +Include paths for these symbols come from PMIx's installed headers (found +via `pkg-config` or `--with-pmix=`). The shim at `src/pmix/pmix-internal.h` +and `src/pmix/pmix.c` is PRRTE's integration point — consult it before +reaching for PMIx symbols elsewhere. + +### Check capability flags + +PRRTE uses the `AC_PREPROC_IFELSE` idiom in its configure script to test for PMIx capability flags at build time. PRRTE's `config/prte_setup_pmix.m4` defines a helper macro `PRTE_CHECK_PMIX_CAP` that reduces the check to: + +```m4 +PRTE_CHECK_PMIX_CAP([MY_NEW_FEATURE], + [action if supported], + [action if not supported]) +``` + +The macro succeeds when `PMIX_CAP_MY_NEW_FEATURE` is defined in the installed `pmix_version.h`; it fails (and triggers the third argument) when the definition is absent, meaning the running PMIx predates the feature. + +Code requiring a specific PMIx feature that is covered by a capability flag must be protected by an appropriate `#if FOO` clause. + +--- + +## Coding Rules + +These rules apply to **all** PRRTE C source files without exception. + +### Mandatory header order + +`prte_config.h` **must be the first `#include`** in every `.c` file. +Nothing comes before it — not system headers, not other PRRTE headers. + +```c +#include "prte_config.h" + +#include /* system headers follow */ +... +#include "src/util/name_fns.h" /* then PRRTE/PMIx headers */ +``` + +### Symbol prefixes + +| Scope | Prefix | +|-------|--------| +| Exported (tools, public data) | `PRTE_` (macros) / `prte_` (functions, types) | +| Internal only | `prte_` with no `PRTE_EXPORT` annotation | +| MCA component | `prte___` | + +Do not use `PMIX_` or `pmix_` prefixes for new PRRTE symbols. + +### New files need the standard copyright/license header. + +Copy the multi-institution BSD header block — including the `Copyright (c) 2026 Nanook Consulting All rights reserved. +Copy the multi-institution BSD header block — including the `$COPYRIGHT$` and +`$HEADER$` tokens — from a neighboring file. If you substantially +change an existing file, add your copyright line to its block. + +### `#define` logical macros to `0` or `1`; never `#undef` them. + +Test with `#if FOO`, not `#ifdef FOO`, so a misspelling is a compiler +error, not a silent false. + +### Constant-on-left comparisons + +Always put the constant or NULL on the left side of an equality test: + +```c +if (NULL == ptr) /* correct */ +if (ptr == NULL) /* wrong — easy to mistype as assignment */ + +if (PRTE_SUCCESS == rc) /* correct */ +``` + +### Always brace blocks + +Use `{ }` around every conditional or loop body, even single-line ones. + +### Indentation + +4 spaces, never tab characters. + + +### Stay compiler-warning-free + +PRRTE strives to build with zero compiler warnings. Do not introduce +code that adds new warnings. `--enable-devel-check` is implied when +building from a git repo with `--enable-debug`; it instructs the compiler +to treat all warnings as errors and enables maximum warning coverage +(e.g., unused variables). CI runs with this setting, so your code must +be warning-free before submitting. + +### No hand-editing of generated files + +Do not modify files produced by autotools (`configure`, `Makefile.in`, etc.), pre-rendered documentation, or third-party vendored code. Edit the source code instead. + + +### Error handling + +Functions that can fail return `int`. Check every return value. Use +`PRTE_ERROR_LOG(rc)` to record unexpected errors. Use +`PRTE_ACTIVATE_JOB_STATE(jdata, PRTE_JOB_STATE_*)` to trigger state +transitions rather than handling errors inline in launch paths. + +### Thread model + +PRRTE is event-driven and single-threaded on the progress thread. Use +the PRRTE event loop (`prte_event_base`) for deferred work. Do not block on +the progress thread. + +### Thread-shifting with caddies + +A **caddy** is a short-lived heap object whose sole job is to carry a request's parameters across states within the progress thread. Every caddy struct must contain at minimum: + +| Field | Type | Purpose | +|-------|------|---------| +| ev | `pmix_event_t` | Required by Libevent to queue the caddy; **must be named `ev`** | +| lock | `pmix_lock_t` | Thread synchronization (blocking operations wait on this; handlers wake it) | +| cbdata | `void *` | Opaque pointer passed through to the callback | +| callback pointer(s) | function pointer(s) | Cache the caller-supplied callback function(s) | + +The pattern: + +1. Allocate a caddy with `PMIX_NEW(caddy_type_t)`. +2. Assign the caddy's fields to point at the caller's parameters — **do not copy the data**. +3. Call `PRTE_PMIX_THREADSHIFT(cd, evbase, handler_fn)` to post the caddy to the progress thread's event queue. +4. The progress thread fires `handler_fn(cd)`, which performs the actual work. + +Never read or write shared library state outside of the progress thread; do it only inside the handler that runs on the progress thread. +Do not allocate a caddy on the stack — it must outlive the function that creates it. + + +### Memory management + +Use `PMIX_NEW` / `PMIX_RELEASE` (PMIx's reference-counted object system) +for all PRRTE objects that embed `pmix_object_t`. Raw allocations use +`malloc`/`free` directly — avoid `PMIX_MALLOC`/`PMIX_FREE` for plain +buffers unless interfacing with PMIx routines that require it. + +### C standard + +PRRTE targets C11. Do not add `-Wno-*` flags to suppress warnings — +fix the underlying issue. + +--- + +## Build System + +PRRTE uses GNU Autotools. The generated `configure` script is **not** in +the git repository; it is produced by running: + +```sh +./autogen.pl +``` + +This must be re-run whenever `configure.ac`, any `Makefile.am`, or any +`*.m4` file under `config/` is modified. After `autogen.pl`: + +```sh +./configure [options] +make -j$(nproc) +make install +``` + +Common configure options: + +| Option | Purpose | +|--------|---------| +| `--with-pmix=` | Path to PMIx installation (required if not in default search path) | +| `--with-hwloc=` | Path to hwloc installation | +| `--with-libevent=` | Path to libevent installation | +| `--with-slurm` | Enable SLURM support | +| `--with-lsf=` | Enable LSF support | +| `--with-pbs` | Enable PBS/Torque support | +| `--with-flux=` | Enable Flux support | +| `--enable-debug` | Build with debug symbols and extra assertions | +| `--enable-devel-check` | Enable strict compiler warnings (treat warnings as errors); on by default when `--enable-debug` is used in a git repo build | + +Version requirements: PMIx ≥ 6.1.0, hwloc ≥ 2.1.0, libevent ≥ 2.0.21. + +--- + +## State Machines + +PRRTE drives both the DVM lifecycle and individual job lifecycles through +explicit state machines defined in `src/mca/state/`. Process and job +states are declared in `src/mca/state/state_types.h`. When debugging +launch or termination problems, enable framework verbosity to trace +state transitions: + +```sh +prte --prtemca state_base_verbose 5 ... # trace job state transitions +prte --prtemca plm_base_verbose 5 ... # trace daemon launch +prte --prtemca rmaps_base_verbose 5 ... # trace process mapping +``` + +Key job states in order: `PRTE_JOB_STATE_INIT` → +`PRTE_JOB_STATE_ALLOCATE` → `PRTE_JOB_STATE_MAP` → +`PRTE_JOB_STATE_LAUNCH_DAEMONS` → `PRTE_JOB_STATE_RUNNING` → +`PRTE_JOB_STATE_TERMINATED`. + +--- + +## Contributing + +### Commit messages + +Write prose commit messages, not bullet lists. The subject line should +complete the sentence "If applied, this commit will …". The body must +explain **why** the change is needed, not just what it does. Keep the +subject line under 72 characters. + +All commits require a `Signed-off-by:` line (DCO): + +``` +git commit -s +``` + +### Pull requests + +- Open PRs against the `master` branch (development trunk). +- One logical change per PR. Split unrelated fixes. +- Describe the problem being solved in the PR description, not just the + solution. +- Documentation updates (`docs/`) are required for user-visible changes. + +### Testing + +PRRTE does not have a standalone unit test suite. Integration-level +testing is done by running actual parallel jobs through the DVM. When +modifying launch, mapping, or I/O forwarding paths, test with: + +```sh +prte --daemonize # start DVM +prun -n 4 hostname # basic launch smoke test +pterm # shut down DVM +``` + +For resource manager integration (SLURM, PBS, LSF), test within an actual +allocation on the relevant system. + +### Reporting bugs + +File issues at https://github.com/openpmix/prrte/issues. Include the +output of `prte_info --all` and a minimal reproduction case. From 9828d963c6a3e5ab0f13db7a2a9befe1ebea86a4 Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Wed, 17 Jun 2026 16:18:08 -0600 Subject: [PATCH 02/10] docs: add community Code of Conduct Add a Contributor Covenant v1.4 Code of Conduct for the PRRTE project, following the same pattern used by OpenPMIx. The authoritative source is .github/CODE_OF_CONDUCT.md (Markdown); docs/Makefile.am drives a Python converter script that regenerates docs/code-of-conduct.rst at build time so the content appears in the Sphinx-built HTML documentation. The new page is wired into the docs/index.rst toctree between the Contributing and License sections. Signed-off-by: Ralph Castain (cherry picked from commit dfe14c67bda75930b76819cfd6324668ce77ea81) --- .github/CODE_OF_CONDUCT.md | 46 ++++++++++++ docs/Makefile.am | 18 ++++- docs/generate-code-of-conduct-rst.py | 103 +++++++++++++++++++++++++++ docs/index.rst | 1 + 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 docs/generate-code-of-conduct-rst.py diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..4d81c2e38a --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# PRRTE Community Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the PRRTE project or its community. Examples of representing the project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of the project may be further defined and clarified by PRRTE maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at maintainers@pmix.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/docs/Makefile.am b/docs/Makefile.am index db02b64a69..f458512f8a 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -31,6 +31,9 @@ OUTDIR = _build SPHINX_CONFIG = conf.py SPHINX_OPTS ?= -W --keep-going -j auto +MARKDOWN_SOURCE_FILES = \ + $(top_srcdir)/.github/CODE_OF_CONDUCT.md + # Note: it is significantly more convenient to list all the source # files here using wildcards (vs. listing every single .rst file). # However, it is necessary to list $(srcdir) when using wildcards. @@ -50,8 +53,10 @@ RST_SOURCE_FILES = \ EXTRA_DIST = \ requirements.txt \ + generate-code-of-conduct-rst.py \ _templates/configurator.html \ $(SPHINX_CONFIG) \ + $(MARKDOWN_SOURCE_FILES) \ $(RST_SOURCE_FILES) ########################################################################### @@ -93,6 +98,10 @@ if PRTE_BUILD_DOCS include $(top_srcdir)/Makefile.prte-rules +# Generate this file from CODE_OF_CONDUCT.md as part of the Sphinx +# docs build. +CODE_OF_CONDUCT_RST = $(srcdir)/code-of-conduct.rst + # Have to not list these targets in EXTRA_DIST outside of the # PRTE_BUILD_DOCS conditional because "make dist" will fail due to # these missing targets (and therefore not run the "dist-hook" target @@ -109,6 +118,12 @@ ALL_MAN_BUILT = \ $(PRTE_MAN1_BUILT) $(PRTE_MAN5_BUILT) $(ALL_MAN_BUILT): $(RST_SOURCE_FILES) $(IMAGE_SOURCE_FILES) $(ALL_MAN_BUILT): $(TEXT_SOURCE_FILES) $(SPHINX_CONFIG) +$(ALL_MAN_BUILT): $(CODE_OF_CONDUCT_RST) + +$(CODE_OF_CONDUCT_RST): $(top_srcdir)/.github/CODE_OF_CONDUCT.md +$(CODE_OF_CONDUCT_RST): generate-code-of-conduct-rst.py + $(PRTE_V_GEN) python3 $(srcdir)/generate-code-of-conduct-rst.py \ + --input $(top_srcdir)/.github/CODE_OF_CONDUCT.md --output $@ # List both commands (HTML and man) in a single rule because they # really need to be run in serial. Specifically, if they were two @@ -130,12 +145,13 @@ linkcheck: maintainer-clean-local: $(SPHINX_BUILD) -M clean "$(srcdir)" "$(OUTDIR)" $(SPHINX_OPTS) + rm -f $(CODE_OF_CONDUCT_RST) # List all the built man pages here in the Automake BUILT_SOURCES # macro. This hooks into the normal Automake build mechanisms, and # will ultimately cause the invocation of the above rule that runs # Sphinx to build the HTML and man pages. -BUILT_SOURCES = $(ALL_MAN_BUILT) +BUILT_SOURCES = $(CODE_OF_CONDUCT_RST) $(ALL_MAN_BUILT) endif PRTE_BUILD_DOCS diff --git a/docs/generate-code-of-conduct-rst.py b/docs/generate-code-of-conduct-rst.py new file mode 100644 index 0000000000..3a2cc66d07 --- /dev/null +++ b/docs/generate-code-of-conduct-rst.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2026 Nanook Consulting All rights reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +"""Convert PRRTE's Markdown CODE_OF_CONDUCT.md to reStructuredText.""" + +import argparse +import re +from pathlib import Path + +HEADING_RE = re.compile(r"^(#{1,6})\s+(.+?)\s*#*\s*$") +REF_DEF_RE = re.compile(r"^\[([^\]]+)\]:\s*(\S+)\s*$") +REF_LINK_RE = re.compile(r"\[([^\]]+)\]\[([^\]]+)\]") +INLINE_LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)]+)\)") +BULLET_RE = re.compile(r"^\s*[*+-]\s+") +UNDERLINES = ["=", "-", "~", "^", '"', "'"] + + +def _rst_link(text, url): + return "`{} <{}>`_".format(text, url) + + +def _convert_links(line, refs): + def repl_ref(match): + text = match.group(1) + ref = match.group(2) + return _rst_link(text, refs.get(ref, ref)) + + def repl_inline(match): + return _rst_link(match.group(1), match.group(2)) + + line = REF_LINK_RE.sub(repl_ref, line) + line = INLINE_LINK_RE.sub(repl_inline, line) + return line + + +def convert(lines): + refs = {} + for line in lines: + match = REF_DEF_RE.match(line.strip()) + if match: + refs[match.group(1)] = match.group(2) + + out = [ + "..", + " This file was generated from .github/CODE_OF_CONDUCT.md by docs/Makefile.am.", + " Do not edit directly.", + "", + ] + + skip_next_blank = False + prev_bullet = False + for line in lines: + if REF_DEF_RE.match(line.strip()): + continue + if skip_next_blank and not line.strip(): + skip_next_blank = False + continue + skip_next_blank = False + + match = HEADING_RE.match(line) + if match: + if out and out[-1] != "": + out.append("") + title = _convert_links(match.group(2), refs) + level = min(len(match.group(1)) - 1, len(UNDERLINES) - 1) + out.extend([title, UNDERLINES[level] * len(title), ""]) + skip_next_blank = True + prev_bullet = False + else: + is_bullet = bool(BULLET_RE.match(line)) + if is_bullet and out and out[-1] != "" and not prev_bullet: + out.append("") + out.append(_convert_links(line, refs)) + prev_bullet = is_bullet + + while out and out[-1] == "": + out.pop() + return "\n".join(out) + "\n" + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--input", required=True) + parser.add_argument("--output", required=True) + args = parser.parse_args() + + input_path = Path(args.input) + output_path = Path(args.output) + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text( + convert(input_path.read_text(encoding="utf-8").splitlines()), + encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/docs/index.rst b/docs/index.rst index fbd3161e05..47eafee5b4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -40,6 +40,7 @@ Table of contents session-directory developers/index contributing + code-of-conduct license man/index versions From 7855124569ecf9a94612e4faae67bd763f3743a6 Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Wed, 17 Jun 2026 16:21:22 -0600 Subject: [PATCH 03/10] docs: wire code-of-conduct RST generation into ReadTheDocs ReadTheDocs does not run Automake, so the pre_build job in .readthedocs.yaml must invoke the generator script directly before Sphinx runs. Also add docs/code-of-conduct.rst to .gitignore since it is a generated file and should not be tracked. Signed-off-by: Ralph Castain (cherry picked from commit 3b5598d6900aad9c79cb66e169f9500107b5108d) --- .gitignore | 3 +++ .readthedocs.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 2e103ff9f0..7102cecab7 100644 --- a/.gitignore +++ b/.gitignore @@ -205,6 +205,9 @@ docs/_build docs/_static docs/_static/css/custom.css docs/_templates + +# Generated RST files +docs/code-of-conduct.rst src/docs/_build src/docs/mca/mca.rst src/docs/mca/help*rst diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 44e0bbac5a..3e4cffff2f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,6 +12,9 @@ build: os: ubuntu-22.04 tools: python: "3.10" + jobs: + pre_build: + - python3 docs/generate-code-of-conduct-rst.py --input .github/CODE_OF_CONDUCT.md --output docs/code-of-conduct.rst python: install: From 65e761b9814bf78790642960111a6598382db4aa Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Wed, 17 Jun 2026 19:16:09 -0600 Subject: [PATCH 04/10] fix: rmaps/lsf mapper broken when LSB_AFFINITY_HOSTFILE is set but empty LSF sets LSB_AFFINITY_HOSTFILE for every job, even when the user has not requested process pinning, in which case the file is empty. The new rmaps/lsf component introduced in v3.0.13 fires whenever that environment variable is present, calls file_parse(), which returns PRTE_SUCCESS immediately on an empty file leaving num_ranks at zero, and then incorrectly triggers a fatal "bad-syntax" error instead of stepping aside. In v3.0.12 the equivalent code path returned from the rank_file mapper and let round_robin take over. Five issues are addressed: - When file_parse() returns with num_ranks == 0 (empty affinity file), return PRTE_ERR_TAKE_NEXT_OPTION so normal mapping proceeds. This fixes the reported regression (GitHub issue #2449). - Reset the static num_ranks counter to 0 at the top of lsf_map() and in the error path. The counter was never cleared between jobs, so the second and subsequent jobs in a DVM session would store rankmap entries at wrong indices and read back NULLs, silently producing incorrect process placement. - Move "options->map = PRTE_MAPPING_BYUSER" to just after last_mapper is set, before the per-app loop. It was inside the per-process inner loop, so prte_rmaps_base_get_target_nodes() was called on the first app with the wrong map type. The rank_file mapper sets this flag in the same early position. - Fix the error: path to destruct rankmap and release its rfmap entries, matching what the success path already does. Previously only node_list was cleaned up on error. - Remove the dead assigned_ranks_array from file_parse(). It was allocated and initialized (copied from the rank_file parser) but never written to, read, or freed. Also initialize sep = NULL to prevent use of an indeterminate value if a malformed affinity-file line contains no space separator. Signed-off-by: Ralph Castain (cherry picked from commit a01985a5aaba37b96664fc8443949d0da013b8b4) --- src/mca/rmaps/lsf/rmaps_lsf.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/mca/rmaps/lsf/rmaps_lsf.c b/src/mca/rmaps/lsf/rmaps_lsf.c index f73aeb8171..ae2a6f8c68 100644 --- a/src/mca/rmaps/lsf/rmaps_lsf.c +++ b/src/mca/rmaps/lsf/rmaps_lsf.c @@ -142,6 +142,7 @@ static int lsf_map(prte_job_t *jdata, free(jdata->map->last_mapper); } jdata->map->last_mapper = strdup(c->pmix_mca_component_name); + options->map = PRTE_MAPPING_BYUSER; /* setup the node list */ PMIX_CONSTRUCT(&node_list, pmix_list_t); @@ -156,6 +157,7 @@ static int lsf_map(prte_job_t *jdata, /* start at the beginning... */ vpid_start = 0; jdata->num_procs = 0; + num_ranks = 0; PMIX_CONSTRUCT(&rankmap, pmix_pointer_array_t); rc = pmix_pointer_array_init(&rankmap, PRTE_GLOBAL_ARRAY_BLOCK_SIZE, @@ -171,6 +173,13 @@ static int lsf_map(prte_job_t *jdata, rc = PRTE_ERR_SILENT; goto error; } + if (0 == num_ranks) { + /* empty affinity file means LSF set no pinning for this job - + * let the next mapper handle it normally */ + PMIX_DESTRUCT(&rankmap); + PMIX_LIST_DESTRUCT(&node_list); + return PRTE_ERR_TAKE_NEXT_OPTION; + } /* cycle through the app_contexts, mapping them sequentially */ for (i = 0; i < jdata->apps->size; i++) { @@ -312,7 +321,6 @@ static int lsf_map(prte_job_t *jdata, if (PRTE_SUCCESS != rc) { goto error; } - options->map = PRTE_MAPPING_BYUSER; proc = prte_rmaps_base_setup_proc(jdata, app->idx, node, NULL, options); if (NULL == proc) { PRTE_ERROR_LOG(PRTE_ERR_OUT_OF_RESOURCE); @@ -432,34 +440,28 @@ static int lsf_map(prte_job_t *jdata, error: PMIX_LIST_DESTRUCT(&node_list); + for (i = 0; i < rankmap.size; i++) { + if (NULL != (rfmap = pmix_pointer_array_get_item(&rankmap, i))) { + PMIX_RELEASE(rfmap); + } + } + PMIX_DESTRUCT(&rankmap); + num_ranks = 0; return rc; } static int file_parse(const char *affinity_file) { - int rc = PRTE_SUCCESS; int i, j; prte_rmaps_lsf_map_t *rfmap = NULL; - pmix_pointer_array_t *assigned_ranks_array; struct stat buf; FILE *fp; char *hstname, *membind_opt; - char *sep, *eptr, **cpus, *ptr; + char *sep = NULL, *eptr, **cpus, *ptr; prte_node_t *nptr, *node; hwloc_obj_t obj; - /* keep track of rank assignments */ - assigned_ranks_array = PMIX_NEW(pmix_pointer_array_t); - rc = pmix_pointer_array_init(assigned_ranks_array, - PRTE_GLOBAL_ARRAY_BLOCK_SIZE, - PRTE_GLOBAL_ARRAY_MAX_SIZE, - PRTE_GLOBAL_ARRAY_BLOCK_SIZE); - if (PMIX_SUCCESS != rc) { - PMIX_RELEASE(assigned_ranks_array); - return PRTE_ERROR; - } - /* check to see if the file is empty - if it is, * then affinity wasn't actually set for this job */ if (0 != stat(affinity_file, &buf)) { From a22c3d7f48ba4d4f696675525aa09532a1f4bead Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Thu, 18 Jun 2026 09:17:58 -0600 Subject: [PATCH 05/10] ras: centralize FQDN/short-name normalization in the RAS base All RAS components (lsf, slurm, pbs, flux, gridengine, hosts) create prte_node_t objects and hand them to prte_ras_base_node_insert, but none of them applied the FQDN-to-short-name normalization that hostfile.c and dash_host.c had been doing independently. This meant nodes sourced directly from a scheduler API (e.g. lsb_getalloc()) were inserted into prte_node_pool with whatever hostname the scheduler returned, with no short-name alias, so prte_quickmatch and prte_nptr_match could fail when the affinity file or -host list used a different form of the same name. Add a static normalize_node() helper in ras_base_node.c. When keep_fqdn_hostnames is not set and a node name contains a dot, it copies the full FQDN, truncates node->name in place to the short form, stores the FQDN in node->rawname (or in aliases if rawname is already populated), and appends rawname to node->aliases. normalize_node() is called for every new node just before it is inserted into prte_node_pool, after the existing FQDN-detection check so that prte_have_fqdn_allocation is still set correctly. Remove the equivalent per-site normalization code from hostfile.c (STRING, RELATIVE, and RANK token branches) and dash_host.c. Both utilities now create node objects with the raw name from the file or command line; the RAS base normalizes on insertion. The matching functions (prte_quickmatch, prte_node_match, prte_nptr_match) all check node->aliases, so nodes produced by these utilities in the rmaps filter path continue to match against pool nodes correctly because the pool node's FQDN alias satisfies the alias check even when the filter node carries only the original unstripped name. Signed-off-by: Ralph Castain (cherry picked from commit ea2b8e6f34eefd1c8ee8b7eaa00ab4ced6cd7d9e) --- src/mca/ras/base/ras_base_node.c | 35 +++++++++++-- src/util/dash_host/dash_host.c | 59 ++------------------- src/util/hostfile/hostfile.c | 89 ++------------------------------ 3 files changed, 37 insertions(+), 146 deletions(-) diff --git a/src/mca/ras/base/ras_base_node.c b/src/mca/ras/base/ras_base_node.c index 627eececda..87e4bca1be 100644 --- a/src/mca/ras/base/ras_base_node.c +++ b/src/mca/ras/base/ras_base_node.c @@ -40,6 +40,32 @@ #include "src/mca/ras/base/base.h" +/* Normalize a node name to the short form, storing the FQDN as rawname + * and as an alias so both forms resolve via prte_quickmatch / prte_nptr_match. + * No-op when keep_fqdn_hostnames is set or the name is an IP address. */ +static void normalize_node(prte_node_t *node) +{ + char *ptr, *fqdn; + + if (prte_keep_fqdn_hostnames || pmix_net_isaddr(node->name)) { + return; + } + ptr = strchr(node->name, '.'); + if (NULL == ptr) { + return; + } + /* copy the full FQDN, then truncate node->name in place to the short name */ + fqdn = strdup(node->name); + *ptr = '\0'; + if (NULL == node->rawname) { + node->rawname = fqdn; + } else { + PMIx_Argv_append_unique_nosize(&node->aliases, fqdn); + free(fqdn); + } + PMIx_Argv_append_unique_nosize(&node->aliases, node->rawname); +} + /* * Add the specified node definitions to the global data store * NOTE: this removes all items from the list! @@ -197,6 +223,11 @@ int prte_ras_base_node_insert(pmix_list_t *nodes, prte_job_t *jdata) */ PRTE_FLAG_SET(node, PRTE_NODE_FLAG_SLOTS_GIVEN); } + /* detect FQDN before normalizing, then normalize to short name */ + if (!pmix_net_isaddr(node->name) && NULL != strchr(node->name, '.')) { + prte_have_fqdn_allocation = true; + } + normalize_node(node); /* insert it into the array */ node->index = pmix_pointer_array_add(prte_node_pool, (void *) node); if (PRTE_SUCCESS > (rc = node->index)) { @@ -221,10 +252,6 @@ int prte_ras_base_node_insert(pmix_list_t *nodes, prte_job_t *jdata) } /* update the total slots in the job */ prte_ras_base.total_slots_alloc += node->slots; - /* check if we have fqdn names in the allocation */ - if (!pmix_net_isaddr(node->name) && NULL != strchr(node->name, '.')) { - prte_have_fqdn_allocation = true; - } /* duplicate the node if requested */ for (i = 1; i < prte_ras_base.multiplier; i++) { rc = prte_node_copy(&nptr, node); diff --git a/src/util/dash_host/dash_host.c b/src/util/dash_host/dash_host.c index 04d81e3177..668b7fe969 100644 --- a/src/util/dash_host/dash_host.c +++ b/src/util/dash_host/dash_host.c @@ -93,8 +93,6 @@ int prte_util_add_dash_host_nodes(pmix_list_t *nodes, char *hosts, bool allocati int slots = 0; bool slots_given; char *cptr; - char *shortname; - char *rawname; bool add_slots = false; PMIX_OUTPUT_VERBOSE((1, prte_ras_base_framework.framework_output, @@ -250,34 +248,15 @@ int prte_util_add_dash_host_nodes(pmix_list_t *nodes, char *hosts, bool allocati } } - /* check for local name and compute non-fqdn name */ - shortname = NULL; - rawname = NULL; - + /* check for local name */ if (prte_check_host_is_local(mini_map[i])) { ndname = prte_process_info.nodename; } else { ndname = mini_map[i]; } - if (!prte_keep_fqdn_hostnames) { - // Strip off the FQDN if present, ignore IP addresses - if (!pmix_net_isaddr(mini_map[i])) { - cptr = strchr(ndname, '.'); - if (NULL != cptr) { - rawname = strdup(ndname); - *cptr = '\0'; - shortname = strdup(ndname); - *cptr = '.'; - } - } - } - /* see if a node of this name is already on the list */ node = prte_node_match(&adds, ndname); - if (NULL == node && NULL != shortname) { - node = prte_node_match(&adds, shortname); - } if (NULL != node) { if (slots_given) { node->slots += slots; @@ -296,36 +275,14 @@ int prte_util_add_dash_host_nodes(pmix_list_t *nodes, char *hosts, bool allocati PMIX_OUTPUT_VERBOSE((1, prte_ras_base_framework.framework_output, "%s dashhost: node %s already on list - slots %d", PRTE_NAME_PRINT(PRTE_PROC_MY_NAME), node->name, node->slots)); - if (NULL != shortname) { - free(shortname); - shortname = NULL; - } - if (NULL != rawname) { - node->rawname = rawname; - rawname = NULL; - } } else { /* if we didn't find it, add it to the list */ node = PMIX_NEW(prte_node_t); if (NULL == node) { PMIx_Argv_free(mapped_nodes); - if (NULL != shortname) { - free(shortname); - } - if (NULL != rawname) { - free(rawname); - } return PRTE_ERR_OUT_OF_RESOURCE; } - if (prte_keep_fqdn_hostnames || NULL == shortname) { - node->name = strdup(ndname); - } else { - node->name = strdup(shortname); - } - if (NULL != rawname) { - node->rawname = rawname; - rawname = NULL; - } + node->name = strdup(ndname); PMIX_OUTPUT_VERBOSE((1, prte_ras_base_framework.framework_output, "%s dashhost: added node %s to list - slots %d", PRTE_NAME_PRINT(PRTE_PROC_MY_NAME), node->name, slots)); @@ -349,19 +306,9 @@ int prte_util_add_dash_host_nodes(pmix_list_t *nodes, char *hosts, bool allocati pmix_list_append(&adds, &node->super); } if (0 != strcmp(node->name, mini_map[i])) { - // add the mini_map name to the list of aliases + // add the mini_map entry as an alias if it differs from the resolved name PMIx_Argv_append_unique_nosize(&node->aliases, mini_map[i]); } - // ensure the non-fqdn version is saved - if (NULL != shortname && 0 != strcmp(shortname, node->name)) { - PMIx_Argv_append_unique_nosize(&node->aliases, shortname); - } - if (NULL != shortname) { - free(shortname); - } - if (NULL != rawname) { - free(rawname); - } } PMIx_Argv_free(mini_map); diff --git a/src/util/hostfile/hostfile.c b/src/util/hostfile/hostfile.c index b10ee35b5b..4f16c3ad3f 100644 --- a/src/util/hostfile/hostfile.c +++ b/src/util/hostfile/hostfile.c @@ -115,7 +115,6 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, int cnt; int number_of_slots = 0; char buff[64]; - char *alias = NULL; if (PRTE_HOSTFILE_STRING == token || PRTE_HOSTFILE_HOSTNAME == token || PRTE_HOSTFILE_INT == token || PRTE_HOSTFILE_IPV4 == token || @@ -142,17 +141,6 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, } PMIx_Argv_free(argv); - if (!prte_keep_fqdn_hostnames) { - // Strip off the FQDN if present, ignore IP addresses - if (!pmix_net_isaddr(node_name)) { - char *ptr; - alias = strdup(node_name); - if (NULL != (ptr = strchr(node_name, '.'))) { - *ptr = '\0'; - } - } - } - /* if the first letter of the name is '^', then this is a node * to be excluded. Remove the ^ character so the nodename is * usable, and put it on the exclude list @@ -181,20 +169,11 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, node = prte_node_match(exclude, node_name); if (NULL == node) { node = PMIX_NEW(prte_node_t); - if (prte_keep_fqdn_hostnames || NULL == alias) { - node->name = strdup(node_name); - } else { - node->name = strdup(alias); - node->rawname = strdup(node_name); - } + node->name = strdup(node_name); if (NULL != username) { prte_set_attribute(&node->attributes, PRTE_NODE_USERNAME, PRTE_ATTR_LOCAL, username, PMIX_STRING); } - if (NULL != alias && 0 != strcmp(alias, node->name)) { - // new node object, so alias must be unique - PMIx_Argv_append_nosize(&node->aliases, alias); - } pmix_list_append(exclude, &node->super); } else { /* the node name may not match the prior entry, so ensure we @@ -203,13 +182,6 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, PMIx_Argv_append_unique_nosize(&node->aliases, node_name); } free(node_name); - if (NULL != alias && 0 != strcmp(alias, node->name)) { - PMIx_Argv_append_unique_nosize(&node->aliases, alias); - } - } - if (NULL != alias) { - free(alias); - alias = NULL; } if (NULL != username) { free(username); @@ -237,22 +209,13 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, /* Do we need to make a new node object? */ if (keep_all || NULL == (node = prte_node_match(updates, node_name))) { node = PMIX_NEW(prte_node_t); - if (prte_keep_fqdn_hostnames || NULL == alias) { - node->name = strdup(node_name); - } else { - node->name = strdup(node_name); - node->rawname = strdup(alias); - } + node->name = strdup(node_name); free(node_name); node->slots = 1; if (NULL != username) { prte_set_attribute(&node->attributes, PRTE_NODE_USERNAME, PRTE_ATTR_LOCAL, username, PMIX_STRING); } - if (NULL != alias && 0 != strcmp(alias, node->name)) { - // new node object, so alias must be unique - PMIx_Argv_append_nosize(&node->aliases, alias); - } pmix_list_append(updates, &node->super); } else { /* this node was already found once - add a slot and mark slots as "given" */ @@ -264,44 +227,13 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, PMIx_Argv_append_unique_nosize(&node->aliases, node_name); } free(node_name); - if (NULL != alias && 0 != strcmp(alias, node->name)) { - PMIx_Argv_append_unique_nosize(&node->aliases, alias); - } - } - if (NULL != alias) { - free(alias); - alias = NULL; } } else if (PRTE_HOSTFILE_RELATIVE == token) { /* store this for later processing */ node = PMIX_NEW(prte_node_t); - // Strip off the FQDN if present, ignore IP addresses - if (!pmix_net_isaddr(prte_util_hostfile_value.sval)) { - char *ptr; - alias = strdup(prte_util_hostfile_value.sval); - if (NULL != (ptr = strchr(alias, '.'))) { - *ptr = '\0'; - } else { - free(alias); - alias = NULL; - } - } - if (prte_keep_fqdn_hostnames || NULL == alias) { - node->name = strdup(prte_util_hostfile_value.sval); - } else { - node->name = strdup(alias); - node->rawname = strdup(prte_util_hostfile_value.sval); - } - if (NULL != alias && 0 != strcmp(alias, node->name)) { - // new node object, so alias must be unique - PMIx_Argv_append_nosize(&node->aliases, alias); - } + node->name = strdup(prte_util_hostfile_value.sval); pmix_list_append(updates, &node->super); - if (NULL != alias) { - free(alias); - alias = NULL; - } } else if (PRTE_HOSTFILE_RANK == token) { /* we can ignore the rank, but we need to extract the node name. we @@ -339,15 +271,6 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, } PMIx_Argv_free(argv); - // Strip off the FQDN if present, ignore IP addresses - if (!prte_keep_fqdn_hostnames && !pmix_net_isaddr(node_name)) { - char *ptr; - alias = strdup(node_name); - if (NULL != (ptr = strchr(alias, '.'))) { - *ptr = '\0'; - } - } - /* Do we need to make a new node object? */ if (NULL == (node = prte_node_match(updates, node_name))) { node = PMIX_NEW(prte_node_t); @@ -367,12 +290,6 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, PMIx_Argv_append_unique_nosize(&node->aliases, node_name); } } - if (NULL != alias) { - PMIx_Argv_append_unique_nosize(&node->aliases, alias); - free(alias); - node->rawname = strdup(node_name); - alias = NULL; - } PMIX_OUTPUT_VERBOSE((1, prte_ras_base_framework.framework_output, "%s hostfile: node %s slots %d nodes-given %s", PRTE_NAME_PRINT(PRTE_PROC_MY_NAME), node->name, node->slots, From ff311e94c5cb8e433a1880cd486531336e5e04ca Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Thu, 18 Jun 2026 09:24:27 -0600 Subject: [PATCH 06/10] fix: remove stale 'alias' reference left after FQDN normalization refactor Commit 8ddaa19634 removed the alias variable from hostfile_parse_line when centralizing FQDN/short-name normalization into the RAS base, but left behind the alias %s format specifier and its argument in the PMIX_OUTPUT_VERBOSE call at the top of the include-node path. Drop both so the code compiles. Signed-off-by: Ralph Castain (cherry picked from commit 38c7eb9a7fb1b695cbb5d4fd882c548773eb0ae9) --- src/util/hostfile/hostfile.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util/hostfile/hostfile.c b/src/util/hostfile/hostfile.c index 4f16c3ad3f..4151590c10 100644 --- a/src/util/hostfile/hostfile.c +++ b/src/util/hostfile/hostfile.c @@ -194,10 +194,9 @@ static int hostfile_parse_line(int token, pmix_list_t *updates, */ PMIX_OUTPUT_VERBOSE((3, prte_ras_base_framework.framework_output, - "%s hostfile: node %s is being included - keep all is %s alias %s", + "%s hostfile: node %s is being included - keep all is %s", PRTE_NAME_PRINT(PRTE_PROC_MY_NAME), node_name, - keep_all ? "TRUE" : "FALSE", - (NULL == alias) ? "NULL" : alias)); + keep_all ? "TRUE" : "FALSE")); /* see if this is another name for us */ if (prte_check_host_is_local(node_name)) { From 0aee07aeaa47545267144cd5076716928e94b38d Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Thu, 18 Jun 2026 15:01:03 -0600 Subject: [PATCH 07/10] Cleanup stale OpenMPI references in docs Update them to PRRTE Signed-off-by: Ralph Castain (cherry picked from commit cbbf009745631350bf17b7c96583759ce0f7996c) --- docs/developers/gnu-autotools.rst | 2 +- docs/launching-apps/lsf.rst | 7 ++++++- docs/launching-apps/quickstart.rst | 2 +- docs/launching-apps/slurm.rst | 2 +- docs/launching-apps/tm.rst | 4 ++-- docs/launching-apps/unusual.rst | 4 ++-- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/developers/gnu-autotools.rst b/docs/developers/gnu-autotools.rst index bdaee51c0c..9a2b7c081a 100644 --- a/docs/developers/gnu-autotools.rst +++ b/docs/developers/gnu-autotools.rst @@ -88,7 +88,7 @@ to at least the versions listed below. :class: error The table below is almost certainly wrong; it has all the values - from Open MPI. Need to update the table below with the appropriate + from PRRTE. Need to update the table below with the appropriate values for PRRTE. .. list-table:: diff --git a/docs/launching-apps/lsf.rst b/docs/launching-apps/lsf.rst index eeed2c88d3..8e8aca91af 100644 --- a/docs/launching-apps/lsf.rst +++ b/docs/launching-apps/lsf.rst @@ -1,13 +1,18 @@ Launching with LSF ================== +.. warning:: Problems have been reported with using the most recent releases of LSF, in particular + the version supplied with IBM Spectrum LSF Version 10.1 Fix Pack 15. + The suggested workaround is to use an older release of the LSF 10.1 package or to + configure PRRTE without LSF support. + PRRTE supports the LSF resource manager. Verify LSF support ------------------ The ``prte_info`` command can be used to determine whether or not an -installed Open MPI includes LSF support: +installed PRRTE includes LSF support: .. code-block:: diff --git a/docs/launching-apps/quickstart.rst b/docs/launching-apps/quickstart.rst index 73c78e6ee3..3427b1ae9c 100644 --- a/docs/launching-apps/quickstart.rst +++ b/docs/launching-apps/quickstart.rst @@ -7,7 +7,7 @@ Although this section skips many details, it offers examples that will probably work in many environments. .. caution:: Note that this section is a "Quick start" |mdash| it does - not attempt to be comprehensive or describe how to build Open MPI + not attempt to be comprehensive or describe how to build PRRTE in all supported environments. The examples below may therefore not work exactly as shown in your environment. diff --git a/docs/launching-apps/slurm.rst b/docs/launching-apps/slurm.rst index 154d703af1..046b74d4c1 100644 --- a/docs/launching-apps/slurm.rst +++ b/docs/launching-apps/slurm.rst @@ -33,7 +33,7 @@ For example: shell$ salloc -n 4 salloc: Granted job allocation 1234 - # Now run an Open MPI job on all the slots allocated by Slurm + # Now run an PRRTE job on all the slots allocated by Slurm shell$ prterun mpi-hello-world This will run the 4 processes on the node(s) that were allocated diff --git a/docs/launching-apps/tm.rst b/docs/launching-apps/tm.rst index 13a5ce2c36..a65b1cbe92 100644 --- a/docs/launching-apps/tm.rst +++ b/docs/launching-apps/tm.rst @@ -8,7 +8,7 @@ Verify PBS/Torque support ------------------------- The ``prte_info`` command can be used to determine whether or not an -installed Open MPI includes Torque/PBS Pro support: +installed PRRTE includes Torque/PBS Pro support: .. code-block:: @@ -16,7 +16,7 @@ installed Open MPI includes Torque/PBS Pro support: If the PRRTE installation includes support for PBS/Torque, you should see a line similar to that below. Note the MCA version -information varies depending on which version of Open MPI is +information varies depending on which version of PRRTE is installed. .. code-block:: diff --git a/docs/launching-apps/unusual.rst b/docs/launching-apps/unusual.rst index 193a050915..1b05517a03 100644 --- a/docs/launching-apps/unusual.rst +++ b/docs/launching-apps/unusual.rst @@ -120,14 +120,14 @@ from the command line. Connecting independent MPI applications --------------------------------------- -In certain environments, Open MPI supports connecting multiple, +In certain environments, PRRTE supports connecting multiple, independent MPI applications using mechanisms defined in the MPI specification such as ``MPI_Comm_connect() / MPI_Comm_accept()`` and publishing connection information using ``MPI_Publish_name() / MPI_Lookup_name()``. These mechanisms require a centralized service to exchange contact information across multiple jobs. -Beginning with Open MPI v5.0.0 this can be achieved by starting an +This can be achieved by starting an instance of the prte server with the ``report-uri`` option to display the contact information of the prte server. This information can then be used for launching subsequent MPI applications. From 60d2737bf6072cfbc7690f9af07c2f462147704e Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Sat, 20 Jun 2026 08:50:18 -0600 Subject: [PATCH 08/10] Reject more than one display directive Display directives (map, bindings, allocation, ...) are job-level: there is no way to scope a display to a single app context. When a command line carries more than one - most easily by attaching one to a second app in an MPMD line - silently applying just one would be surprising. Detect this in prte_prun_parse_common_cli(), which both prun and the prte HNP launcher route through, and reject it with the existing "multi-instances" diagnostic and PRTE_ERR_BAD_PARAM rather than promoting an app-level directive to the job (which has no sensible meaning for display). Co-Authored-By: Claude Opus 4.8 Signed-off-by: Ralph Castain (cherry picked from commit 63754e3c3914b317ae7b3a3d0fc27a6f18f84cba) --- src/prted/prun_common.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/prted/prun_common.c b/src/prted/prun_common.c index 1f58c1d832..bbb9e4271f 100644 --- a/src/prted/prun_common.c +++ b/src/prted/prun_common.c @@ -772,6 +772,16 @@ int prte_prun_parse_common_cli(void *jinfo, pmix_cli_result_t *results, /* pass the personality */ PMIX_INFO_LIST_ADD(ret, jinfo, PMIX_PERSONALITY, schizo->name, PMIX_STRING); + /* Display directives are job-level only: there is no way to scope a map, + * binding, or allocation display to an individual app context. A second + * instance therefore almost always means the user attached one to an app + * in an MPMD line, which we cannot honor - reject it rather than silently + * applying just one. */ + if (1 < pmix_cmd_line_get_ninsts(results, PRTE_CLI_DISPLAY)) { + pmix_show_help("help-schizo-base.txt", "multi-instances", true, PRTE_CLI_DISPLAY); + return PRTE_ERR_BAD_PARAM; + } + /* get display options */ opt = pmix_cmd_line_get_param(results, PRTE_CLI_DISPLAY); if (NULL != opt) { From f6ec466b73be3f28369e04bc9e86c4df5f7d8f81 Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Tue, 30 Jun 2026 15:55:34 -0600 Subject: [PATCH 09/10] AGENTS.md: document the __prte_attribute_*__ macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a golden-rule bullet to AGENTS.md pointing AI coding agents at the portable compiler attribute wrappers defined in src/include/prte_config_bottom.h (pulled in transitively by prte_config.h). These macros — __prte_attribute_unused__, __prte_attribute_noreturn__, __prte_attribute_format__, __prte_attribute_deprecated__, and many more — expand to the appropriate __attribute__((...)) on compilers that support it and to nothing elsewhere. The new guidance tells agents to reach for them (for example, to mark an unused function parameter) rather than writing a bare __attribute__ or leaving a compiler warning unaddressed. Docs-only change to the agent guidance file; no code or build impact. Signed-off-by: Ralph Castain (cherry picked from commit 15f1c275577819ad8b4ed09d17fd32f745b73d88) --- AGENTS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 257f6b4334..9b25f47bc4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -202,6 +202,7 @@ Do not use `PMIX_` or `pmix_` prefixes for new PRRTE symbols. ### New files need the standard copyright/license header. +Copy the multi-institution BSD header block — including the `Copyright (c) 2026 Nanook Consulting All rights reserved. Copy the multi-institution BSD header block — including the `$COPYRIGHT$` and `$HEADER$` tokens — from a neighboring file. If you substantially change an existing file, add your copyright line to its block. @@ -292,6 +293,16 @@ buffers unless interfacing with PMIx routines that require it. PRRTE targets C11. Do not add `-Wno-*` flags to suppress warnings — fix the underlying issue. +### Use the `__prte_attribute_*__` macros for compiler attributes.** + [`src/include/prte_config_bottom.h`](src/include/prte_config_bottom.h), + pulled in transitively by `prte_config.h`, defines portable wrappers — + `__prte_attribute_unused__`, `__prte_attribute_noreturn__`, + `__prte_attribute_format__`, `__prte_attribute_deprecated__`, and many + more — that expand to the appropriate `__attribute__((...))` on + compilers that support it and to nothing elsewhere. Reach for these + (for example, to mark an unused function parameter) rather than writing + a bare `__attribute__` or leaving a warning unaddressed. + --- ## Build System From f0b6238bd5acfc8919a1dd701b75f56cd537c14e Mon Sep 17 00:00:00 2001 From: Ralph Castain Date: Thu, 2 Jul 2026 16:19:55 -0600 Subject: [PATCH 10/10] Add missing flag initializer Signed-off-by: Ralph Castain --- src/prted/prun_common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/prted/prun_common.c b/src/prted/prun_common.c index bbb9e4271f..79112b3ce3 100644 --- a/src/prted/prun_common.c +++ b/src/prted/prun_common.c @@ -541,6 +541,7 @@ int prun_common(pmix_cli_result_t *results, } /* we want to be notified upon job completion */ + flag = true; PMIX_INFO_LIST_ADD(ret, jinfo, PMIX_NOTIFY_COMPLETION, &flag, PMIX_BOOL); /* pickup any relevant envars */