diff --git a/.github/workflows/email-test.yaml b/.github/workflows/OFF/email-test.yaml similarity index 58% rename from .github/workflows/email-test.yaml rename to .github/workflows/OFF/email-test.yaml index 9f80476..96974d2 100644 --- a/.github/workflows/email-test.yaml +++ b/.github/workflows/OFF/email-test.yaml @@ -1,5 +1,5 @@ -on: [ pull_request ] +on: [ pull_request, push ] permissions: checks: write @@ -9,10 +9,8 @@ jobs: run: runs-on: [ self-hosted ] steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 2 - - uses: efcs/action/reject-user@main + - uses: actions/checkout@v4 + - uses: efcs/action/get-email@main id: get-email - run: echo ${{ steps.get-email.outputs.user-email }} diff --git a/.github/workflows/python-test.yaml b/.github/workflows/OFF/python-test.yaml similarity index 100% rename from .github/workflows/python-test.yaml rename to .github/workflows/OFF/python-test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/OFF/test.yaml similarity index 100% rename from .github/workflows/test.yaml rename to .github/workflows/OFF/test.yaml diff --git a/.github/workflows/call2.yaml b/.github/workflows/call2.yaml new file mode 100644 index 0000000..0022115 --- /dev/null +++ b/.github/workflows/call2.yaml @@ -0,0 +1,56 @@ +# This file defines pre-commit CI for libc++, libc++abi, and libunwind (on Github). +# +# We split the configurations in multiple stages with the intent of saving compute time +# when a job fails early in the pipeline. This is why the jobs are marked as `continue-on-error: false`. +# We try to run the CI configurations with the most signal in the first stage. +# +# Stages 1 & 2 are meant to be "smoke tests", and are meant to catch most build/test failures quickly and without using +# too many resources. +# Stage 3 is "everything else", and is meant to catch breakages on more niche or unique configurations. +# +# Therefore, we "fail-fast" for any failures during stages 1 & 2, meaning any job failing cancels all other running jobs, +# under the assumption that if the "smoke tests" fail, then the other configurations will likely fail in the same way. +# However, stage 3 does not fail fast, as it's more likely that any one job failing is a flake or a configuration-specific +# + +on: + workflow_dispatch: + inputs: + machine: + description: 'machine' + required: false + default: libcxx-runners-8 + type: choice + options: + - rizzo-runner-8-set + - rizzo-runner-dd-set + - rizzo-runner-1-set + cmd: + description: 'cmd' + required: false + default: echo hello + type: string + image: + description: 'image' + required: false + default: ubuntu:latest + type: string + + + + +jobs: + stage1: + runs-on: ${{ inputs.machine }} + steps: + - name: FooBar + run: ${{ inputs.cmd }} + cont-test: + runs-on: ${{ inputs.machine }} + container: + image: ${{ inputs.image }} + options: --cpus 1 --memory 2G + steps: + - name: echo hello + run: echo hello + diff --git a/.github/workflows/callable.yaml b/.github/workflows/callable.yaml new file mode 100644 index 0000000..f37d61f --- /dev/null +++ b/.github/workflows/callable.yaml @@ -0,0 +1,238 @@ +# This file defines pre-commit CI for libc++, libc++abi, and libunwind (on Github). +# +# We split the configurations in multiple stages with the intent of saving compute time +# when a job fails early in the pipeline. This is why the jobs are marked as `continue-on-error: false`. +# We try to run the CI configurations with the most signal in the first stage. +# +# Stages 1 & 2 are meant to be "smoke tests", and are meant to catch most build/test failures quickly and without using +# too many resources. +# Stage 3 is "everything else", and is meant to catch breakages on more niche or unique configurations. +# +# Therefore, we "fail-fast" for any failures during stages 1 & 2, meaning any job failing cancels all other running jobs, +# under the assumption that if the "smoke tests" fail, then the other configurations will likely fail in the same way. +# However, stage 3 does not fail fast, as it's more likely that any one job failing is a flake or a configuration-specific +# + +on: + workflow_dispatch: + inputs: + source: + description: 'the source root' + required: false + default: . + type: string + cc: + description: 'compiler' + required: false + default: clang + cxx: + description: 'compiler' + required: false + default: clang++ + runner-group: + description: 'runner set' + required: false + default: rizzo-runner-64-set + cmake: + description: 'cmake' + required: false + default: /opt/bin/cmake + ninja: + description: 'ninja' + required: false + default: /opt/bin/ninja + run_stage1: + description: 'Stages to run' + required: false + default: true + run_stage2: + description: 'stage 2 run' + required: false + default: true + run_stage3: + description: 'stage 3 run' + required: false + default: false + + + + + +env: + CMAKE: "/opt/bin/cmake" + # LLVM POST-BRANCH bump version + # LLVM POST-BRANCH add compiler test for ToT - 1, e.g. "Clang 17" + # LLVM RELEASE bump remove compiler ToT - 3, e.g. "Clang 15" + LLVM_HEAD_VERSION: "18" # Used compiler, update POST-BRANCH. + LLVM_PREVIOUS_VERSION: "17" + LLVM_OLDEST_VERSION: "16" + GCC_STABLE_VERSION: "13" + LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-18" + CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics" + + +jobs: + stage1: + if: ${{ inputs.run_stage1 }} + runs-on: + group: ${{ inputs.runner-group }} + continue-on-error: false + strategy: + fail-fast: true + matrix: + config: [ + 'generic-cxx03', + 'generic-cxx26', + 'generic-modules' + ] + cc: [ 'clang-18' ] + cxx: [ 'clang++-18' ] + clang_tidy: [ 'ON' ] + include: + - config: 'generic-gcc' + cc: 'gcc-13' + cxx: 'g++-13' + clang_tidy: 'OFF' + steps: + - uses: actions/checkout@v4 + with: + repository: llvm/llvm-project + ref: ${{ inputs.llvm_ref }} + - name: ${{ matrix.config }}.${{ matrix.cxx }} + run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + ENABLE_CLANG_TIDY: ${{ matrix.clang_tidy }} + - uses: actions/upload-artifact@v3 + if: always() + with: + name: ${{ matrix.config }}-${{ matrix.cxx }}-results + path: | + **/test-results.xml + **/*.abilist + **/CMakeError.log + **/CMakeOutput.log + **/crash_diagnostics/* + stage2: + if: ${{ inputs.run_stage1 }} && ${{ inputs.run_stage2 }} + runs-on: rizzo-runner-64-set + needs: [ stage1 ] + continue-on-error: false + strategy: + fail-fast: true + matrix: + config: [ + 'generic-cxx11', + 'generic-cxx14', + 'generic-cxx17', + 'generic-cxx20', + 'generic-cxx23' + ] + cc: [ 'clang-18' ] + cxx: [ 'clang++-18' ] + clang_tidy: [ 'ON' ] + include: + - config: 'generic-gcc-cxx11' + cc: 'gcc-13' + cxx: 'g++-13' + clang_tidy: 'OFF' + - config: 'generic-cxx23' + cc: 'clang-16' + cxx: 'clang++-16' + clang_tidy: 'OFF' + - config: 'generic-cxx23' + cc: 'clang-17' + cxx: 'clang++-17' + clang_tidy: 'OFF' + steps: + - uses: actions/checkout@v4 + with: + repository: llvm/llvm-project + ref: ${{ inputs.llvm_ref }} + - name: ${{ matrix.config }} + run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + ENABLE_CLANG_TIDY: ${{ matrix.clang_tidy }} + - uses: actions/upload-artifact@v3 + if: always() # Upload artifacts even if the build or test suite fails + with: + name: ${{ matrix.config }}-results + path: | + **/test-results.xml + **/*.abilist + **/CMakeError.log + **/CMakeOutput.log + **/crash_diagnostics/* + stage3: + if: inputs.run_stage3 + needs: [ stage1, stage2 ] + continue-on-error: false + strategy: + fail-fast: false + max-parallel: 8 + matrix: + config: [ + 'generic-abi-unstable', + 'generic-hardening-mode-debug', + 'generic-hardening-mode-extensive', + 'generic-hardening-mode-fast', + 'generic-hardening-mode-fast-with-abi-breaks', + 'generic-merged', + 'generic-modules-lsv', + 'generic-no-exceptions', + 'generic-no-experimental', + 'generic-no-filesystem', + 'generic-no-localization', + 'generic-no-random_device', + 'generic-no-threads', + 'generic-no-tzdb', + 'generic-no-unicode', + 'generic-no-wide-characters', + 'generic-static', + 'generic-with_llvm_unwinder' + ] + machine: [ 'libcxx-runners-8' ] + std_modules: [ 'OFF' ] + include: + - config: 'generic-cxx26' + machine: libcxx-runners-8 + std_modules: 'ON' + - config: 'generic-asan' + machine: libcxx-runners-8 + std_modules: 'OFF' + - config: 'generic-tsan' + machine: libcxx-runners-8 + std_modules: 'OFF' + - config: 'generic-ubsan' + machine: libcxx-runners-8 + std_modules: 'OFF' + # Use a larger machine for MSAN to avoid timeout and memory allocation issues. + - config: 'generic-msan' + machine: libcxx-runners-32 + std_modules: 'OFF' + runs-on: rizzo-runner-64-set + steps: + - uses: actions/checkout@v4 + with: + repository: llvm/llvm-project + ref: ${{ inputs.llvm_ref }} + - name: ${{ matrix.config }} + run: libcxx/utils/ci/run-buildbot ${{ matrix.config }} + env: + CC: clang-18 + CXX: clang++-18 + ENABLE_CLANG_TIDY: "OFF" + ENABLE_STD_MODULES: ${{ matrix.std_modules }} + - uses: actions/upload-artifact@v3 + if: always() + with: + name: ${{ matrix.config }}-results + path: | + **/test-results.xml + **/*.abilist + **/CMakeError.log + **/CMakeOutput.log + **/crash_diagnostics/* diff --git a/.github/workflows/check-run-complete.yaml b/.github/workflows/check-run-complete.yaml new file mode 100644 index 0000000..514517a --- /dev/null +++ b/.github/workflows/check-run-complete.yaml @@ -0,0 +1,21 @@ +name: Next Workflow + +on: + workflow_run: + workflows: ["Previous Workflow"] + types: + - completed + +jobs: + download: + runs-on: ubuntu-latest + + steps: + - name: Download artifact + uses: actions/download-artifact@v2 + with: + name: my-artifact + path: path-to-download-folder + + - name: Unzip artifact + run: unzip path-to-download-folder/my-artifact.zip -d extracted-artifact diff --git a/.github/workflows/email-test2.yaml b/.github/workflows/email-test2.yaml new file mode 100644 index 0000000..c9d269a --- /dev/null +++ b/.github/workflows/email-test2.yaml @@ -0,0 +1,49 @@ + +on: [ pull_request ] + +permissions: + checks: write + statuses: write + +jobs: + run: + runs-on: rizzo-runner-1-set + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: foobar + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo $GITHUB_CONTEXT | jq + - run: git log --pretty=format:%ae | awk '// && !line{line=$0} END{print line}' + - name: Get PR Author Email Address + run: sudo docker ps + - run: | + curl -L \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/users/${{ github.actor }} + - run: | + curl -L \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + ${{ github.event.pull_request._links.commits.href }} + - run: ls -lart && pwd && ls -lart ../ && ls -lart ../../ + - run: git status + - run: python3 ./get-email/main.py + env: + GITHUB_TOKEN: ${{ github.token }} + PULL_REQUEST_COMMITS_HREF: ${{ github.event.pull_request._links.commits.href }} + run-on-success: + # Check if the PR has label libcxx-stage3-tests + if: contains(github.event.pull_request.labels.*.name, 'libcxx-stage3-tests') + needs: [ run ] + runs-on: rizzo-runner-11-set + steps: + - run: | + echo "The name of the pusher: ${{ github.event.pusher.name }}" + + diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml new file mode 100644 index 0000000..5f0028e --- /dev/null +++ b/.github/workflows/label-pr.yaml @@ -0,0 +1,39 @@ +name: "Labelling new pull requests" + +permissions: + contents: read + +on: + pull_request_review: + types: [submitted, edited, dismissed] + +jobs: + # hello + dump: + runs-on: + group: default + steps: + - name: foobar + env: + GITHUB_CONTEXT2: ${{ toJson(github.event.pull_request) }} + run: echo $GITHUB_CONTEXT2 | jq + approved: + if: github.event.review.state == 'APPROVED' + permissions: + issues: write + pull-requests: write + runs-on: + group: rizzo-runner-1-set + steps: + - name: foobar + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + TOKEN: ${{ github.token }} + run: echo $GITHUB_CONTEXT | jq && echo $TOKEN + - run: gh pr edit ${{ github.event.pull_request.html_url }} --add-label "$LABELS" + env: + GITHUB_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + LABELS: libcxx-stage3-tests + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cb70ac3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.*attach_pid* diff --git a/a.txt b/a.txt new file mode 100644 index 0000000..e69de29 diff --git a/configure/action.yaml b/configure/action.yaml new file mode 100644 index 0000000..e2940e1 --- /dev/null +++ b/configure/action.yaml @@ -0,0 +1,49 @@ +name: 'Configure the runtimes build' +description: 'Match strings against paths in diff for origin/main and current SHA' + +inputs: + source: + description: 'the source root' + required: false + default: . + build: + description: 'the build root' + required: false + default: build + install: + description: 'the install root' + required: false + default: install + cc: + description: 'C compiler' + required: true + cxx: + description: 'C++ compiler' + required: true + cmake: + description: 'cmake' + required: false + default: /opt/bin/cmake + ninja: + description: 'ninja' + required: false + default: /opt/bin/ninja + runtimes: + description: 'projects to build' + required: false + default: 'libcxx;libcxxabi' + cmake_args: + description: 'cmake arguments' + required: false + default: '' + +runs: + using: "composite" + + steps: + - run: echo "${{ github.action_path }}" >> $GITHUB_PATH + shell: bash + - run: echo GITHUB_TOKEN=${{ inputs.token }} >> $GITHUB_ENV + shell: bash + - run: source ~/venv/bin/activate && python3 ${{ github.action_path }}/main.py ${{ inputs.test_results }} + shell: bash diff --git a/docker/Dockerfile b/docker/Dockerfile index a8dd2c6..f4d89a3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ -FROM ghcr.io/libcxx/actions-builder:testing AS base +FROM gcr.io/libcxx-buildbots/actions-runner:testing AS base -RUN sudo apt update && sudo apt-get install -y --no-install-recommends python3-pip python3-venv python3-dev python3-full \ +RUN sudo apt update && sudo apt-get install -y --no-install-recommends jq python3-pip python3-venv python3-dev python3-full \ && sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* COPY <> ~/.bashrc RUN . ~/venv/bin/activate && python3 -m pip install --upgrade pip && python3 -m pip install -r /tmp/requirements.txt +RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ + | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ + && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ + && sudo apt-get update \ + && sudo apt-get install gh -y diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index d0ede12..8826ae9 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -3,5 +3,5 @@ services: build: context: . dockerfile: Dockerfile - image: ghcr.io/efcs/efcs-runner:latest + image: gcr.io/libcxx-buildbots/efcs-runner:latest container_name: efcs-runner diff --git a/reject-user/action.yaml b/get-email/action.yaml similarity index 99% rename from reject-user/action.yaml rename to get-email/action.yaml index 06f03fd..1cdc1f4 100644 --- a/reject-user/action.yaml +++ b/get-email/action.yaml @@ -18,3 +18,5 @@ runs: git checkout remotes/origin/${{ github.head_ref }} echo "::set-output name=user_email::$(git log -n 1 --pretty=format:%ae)" git checkout - + + diff --git a/reject-user/main.py b/get-email/main.py similarity index 67% rename from reject-user/main.py rename to get-email/main.py index 66d69c8..99993ad 100644 --- a/reject-user/main.py +++ b/get-email/main.py @@ -3,25 +3,38 @@ from pathlib import Path from typing import Any, Literal -from actions_toolkit.core as core +import actions_toolkit.core as core from actions_toolkit.github import Context from github import Github from pydantic import BaseModel, Field +import requests import rich - # Create Github instance with provided token -github_token = os.environ["GITHUB_TOKEN"] -github = Github(github_token) +github = Github() + # Get current context (GitHub Actions context) context = Context() +import rich # Fetch the repository information repo = github.get_repo("efcs/action") -commit = repo.get_commit(context.sha) -rich.inspect(commit) -rich.print(commit) + + +def get_commits(): + headers = { + 'Accept': 'application/vnd.github+json', + 'Authorization': 'Bearer {}'.format(os.environ['GITHUB_TOKEN']), + 'X-GitHub-Api-Version': '2022-11-28' + } + rich.print(os.environ['PULL_REQUEST_COMMITS_HREF']) + url = os.environ['PULL_REQUEST_COMMITS_HREF'] + + response = requests.get(url, headers=headers) + rich.print(response) + response.raise_for_status() + return response.json() class Annotation(BaseModel): @@ -51,21 +64,10 @@ class CheckRun(BaseModel): def main(): - if commit. - conclusion, annotations = process_results(results) - - check_run = repo.create_check_run( - name="Libc++ Test Suite", - head_sha=context.sha, - status="completed", - conclusion=conclusion, - output={ - "title": "Check Run Output", - "summary": f"{len(annotations)} tests failed.", - "annotations": annotations - }, - ) - rich.print(check_run) + rich.print(os.environ) + rich.print(context) + rich.print(get_commits()) -if __name__ == "__main__": +if __name__ == "__main__": + main() diff --git a/reject-user/results.json b/get-email/results.json similarity index 100% rename from reject-user/results.json rename to get-email/results.json diff --git a/reject-user/run.py b/get-email/run.py similarity index 100% rename from reject-user/run.py rename to get-email/run.py diff --git a/python-test/main.py b/python-test/main.py index eb48a25..17c5e52 100644 --- a/python-test/main.py +++ b/python-test/main.py @@ -22,43 +22,6 @@ commit = repo.get_commit(context.sha) -class TestResult(BaseModel): - code: Literal["PASS", "FAIL", "SKIP", "XPASS"] - elapsed: float - metrics: dict[str, Any] = Field(default_factory=dict) - name: str - output: str - -class LITTestResults(BaseModel): - version: tuple[int, int, int] = Field(alias="__version__") - elapsed: float - tests: list[TestResult] = Field(default_factory=list) - -class Annotation(BaseModel): - path: str - start_line: int - end_line: int - annotation_level: Literal["notice", "warning", "failure"] - message: str - title: str - raw_details: str - -class CheckRun(BaseModel): - name: str - head_sha: str - status: Literal["queued", "in_progress", "completed"] - conclusion: Literal["success", "failure", "neutral", "cancelled", "timed_out", "action_required"] - output: dict[str, Any] = Field(default_factory=dict) - actions: list[dict[str, Any]] = Field(default_factory=list) - check_suite: dict[str, Any] = Field(default_factory=dict) - external_id: str - started_at: str - completed_at: str - output: dict[str, Any] = Field(default_factory=dict) - annotations: list[Annotation] = Field(default_factory=list) - pull_requests: list[dict[str, Any]] = Field(default_factory=list) - - def process_results(results: LITTestResults): annotations = [] conclusion = "success" diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/__pycache__/__init__.cpython-311.pyc b/src/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..e3d741c Binary files /dev/null and b/src/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/llvmact/__init__.py b/src/llvmact/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/llvmact/__pycache__/__init__.cpython-311.pyc b/src/llvmact/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..00e6ca2 Binary files /dev/null and b/src/llvmact/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/llvmact/__pycache__/libcxx_test_parser.cpython-311.pyc b/src/llvmact/__pycache__/libcxx_test_parser.cpython-311.pyc new file mode 100644 index 0000000..d9f9011 Binary files /dev/null and b/src/llvmact/__pycache__/libcxx_test_parser.cpython-311.pyc differ diff --git a/src/llvmact/libcxx_test_parser.py b/src/llvmact/libcxx_test_parser.py new file mode 100644 index 0000000..eaaf5cb --- /dev/null +++ b/src/llvmact/libcxx_test_parser.py @@ -0,0 +1,355 @@ +import re +from typing import Tuple, Union +from pydantic import BaseModel, Field +import os, sys, re +from enum import Enum +import rich +from typing import Any, Optional +import re + +clang_error_re = re.compile(r"^(# \||)(?P.*):(?P\d+):(?P\d+): error: (?P.*)$", re.MULTILINE) + +class ClangError(BaseModel): + file: str + line: int + column : int + text : str + + @staticmethod + def try_parse(text): + m = clang_error_re.match(text) + if m is None: + return None + else: + return ClangError.model_validate({ + 'file': m.group('file'), + 'line': int(m.group('line')), + 'column': int(m.group('column')), + 'text': m.group('text')}) + + +class TryParseResult: + def __init__(self, success: bool, value: Any, raw : str = None): + self.success = success + self._value = value + self._raw = raw if raw is not None else value + + @property + def value(self): + assert self.success + return self._value + + @property + def raw(self): + assert self.success + return self._raw + + def raise_if_failed(self): + if not self.success: + raise ValueError("TryParseResult failed") + return self + + def __bool__(self): + return self.success + + def __str__(self): + return f"TryParseResult(success={self.success}, value={self.value})" + +class StatusBlock(BaseModel): + exit_code : int + stdout : str + groups : list[Any] = Field(default_factory=list) + clang_diagnostics : list[ClangError] = Field(default_factory=list) + + +# A command block is a block of output text from LLVM's test suite defined as: +# either: +# (A) One or more comment lines (the header), followed by one or more non-comment +# lines +class CommandBlock(BaseModel): + header: str = '' + body: str = '' + + +TEST_CASES = [ +''' +Exit Code: 0 + +Command Output (stdout): +-- +# COMPILED WITH +/usr/local/bin/clang++ /home/eric/llvm-project/libcxx/test/std/example/runs.pass.cpp -pthread --target=x86_64-unknown-linux-gnu -nostdinc++ -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -lc++experimental -nostdlib++ -L /home/eric/llvm-project/build/libcxx/lib -Wl,-rpath,/home/eric/llvm-project/build/libcxx/lib -lc++ -o /home/eric/llvm-project/build/libcxx/test/std/example/Output/runs.pass.cpp.dir/t.tmp.exe +# executed command: /usr/local/bin/clang++ /home/eric/llvm-project/libcxx/test/std/example/runs.pass.cpp -pthread --target=x86_64-unknown-linux-gnu -nostdinc++ -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -lc++experimental -nostdlib++ -L /home/eric/llvm-project/build/libcxx/lib -Wl,-rpath,/home/eric/llvm-project/build/libcxx/lib -lc++ -o /home/eric/llvm-project/build/libcxx/test/std/example/Output/runs.pass.cpp.dir/t.tmp.exe +# EXECUTED AS +/home/eric/.pyenvs/idea/bin/python3 /home/eric/llvm-project/libcxx/utils/run.py --execdir /home/eric/llvm-project/build/libcxx/test/std/example/Output/runs.pass.cpp.dir -- /home/eric/llvm-project/build/libcxx/test/std/example/Output/runs.pass.cpp.dir/t.tmp.exe +# executed command: /home/eric/.pyenvs/idea/bin/python3 /home/eric/llvm-project/libcxx/utils/run.py --execdir /home/eric/llvm-project/build/libcxx/test/std/example/Output/runs.pass.cpp.dir -- /home/eric/llvm-project/build/libcxx/test/std/example/Output/runs.pass.cpp.dir/t.tmp.exe + +-- +''', +''' +Exit Code: 250 + +Command Output (stdout): +-- +# COMPILED WITH +/usr/local/bin/clang++ /home/eric/llvm-project/libcxx/test/std/example/fails-to-run.pass.cpp -pthread --target=x86_64-unknown-linux-gnu -nostdinc++ -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -lc++experimental -nostdlib++ -L /home/eric/llvm-project/build/libcxx/lib -Wl,-rpath,/home/eric/llvm-project/build/libcxx/lib -lc++ -o /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-run.pass.cpp.dir/t.tmp.exe +# executed command: /usr/local/bin/clang++ /home/eric/llvm-project/libcxx/test/std/example/fails-to-run.pass.cpp -pthread --target=x86_64-unknown-linux-gnu -nostdinc++ -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -lc++experimental -nostdlib++ -L /home/eric/llvm-project/build/libcxx/lib -Wl,-rpath,/home/eric/llvm-project/build/libcxx/lib -lc++ -o /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-run.pass.cpp.dir/t.tmp.exe +# EXECUTED AS +/home/eric/.pyenvs/idea/bin/python3 /home/eric/llvm-project/libcxx/utils/run.py --execdir /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-run.pass.cpp.dir -- /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-run.pass.cpp.dir/t.tmp.exe +# executed command: /home/eric/.pyenvs/idea/bin/python3 /home/eric/llvm-project/libcxx/utils/run.py --execdir /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-run.pass.cpp.dir -- /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-run.pass.cpp.dir/t.tmp.exe +# .---command stderr------------ +# | t.tmp.exe: /home/eric/llvm-project/libcxx/test/std/example/fails-to-run.pass.cpp:4: int main(): Assertion `false && "shit"' failed. +# `----------------------------- +# error: command failed with exit status: 250 + +-- +''', +''' +Exit Code: 1 + +Command Output (stdout): +-- +# COMPILED WITH +/usr/local/bin/clang++ /home/eric/llvm-project/libcxx/test/std/example/fails-to-compile.pass.cpp -pthread --target=x86_64-unknown-linux-gnu -nostdinc++ -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -lc++experimental -nostdlib++ -L /home/eric/llvm-project/build/libcxx/lib -Wl,-rpath,/home/eric/llvm-project/build/libcxx/lib -lc++ -o /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-compile.pass.cpp.dir/t.tmp.exe +# executed command: /usr/local/bin/clang++ /home/eric/llvm-project/libcxx/test/std/example/fails-to-compile.pass.cpp -pthread --target=x86_64-unknown-linux-gnu -nostdinc++ -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/build/libcxx/include/c++/v1 -I /home/eric/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -lc++experimental -nostdlib++ -L /home/eric/llvm-project/build/libcxx/lib -Wl,-rpath,/home/eric/llvm-project/build/libcxx/lib -lc++ -o /home/eric/llvm-project/build/libcxx/test/std/example/Output/fails-to-compile.pass.cpp.dir/t.tmp.exe +# .---command stderr------------ +# | /home/eric/llvm-project/libcxx/test/std/example/fails-to-compile.pass.cpp:6:7: error: no viable overloaded '+=' +# | 6 | S += V; +# | | ~ ^ ~ +# | /home/eric/llvm-project/libcxx/test/std/example/fails-to-compile.pass.cpp:12:5: note: in instantiation of function template specialization 'foo>' requested here +# | 12 | foo(s, v); +# | | ^ +# | /home/eric/llvm-project/build/libcxx/include/c++/v1/string:1216:71: note: candidate function not viable: no known conversion from 'const std::vector' to 'const string' for 1st argument +# | 1216 | _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator+=(const basic_string& __str) { +# | | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~ +# | /home/eric/llvm-project/build/libcxx/include/c++/v1/string:1225:5: note: candidate template ignored: requirement '__can_be_converted_to_string_view, std::vector>>::value' was not satisfied [with _Tp = std::vector] +# | 1225 | operator+=(const _Tp& __t) { +# | | ^ +# | /home/eric/llvm-project/build/libcxx/include/c++/v1/string:1229:71: note: candidate function not viable: no known conversion from 'const std::vector' to 'const value_type *' (aka 'const char *') for 1st argument +# | 1229 | _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator+=(const value_type* __s) { +# | | ^ ~~~~~~~~~~~~~~~~~~~~~ +# | /home/eric/llvm-project/build/libcxx/include/c++/v1/string:1233:71: note: candidate function not viable: no known conversion from 'const std::vector' to 'value_type' (aka 'char') for 1st argument +# | 1233 | _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator+=(value_type __c) { +# | | ^ ~~~~~~~~~~~~~~ +# | /home/eric/llvm-project/build/libcxx/include/c++/v1/string:1240:19: note: candidate function not viable: no known conversion from 'const std::vector' to 'initializer_list' (aka 'initializer_list') for 1st argument +# | 1240 | basic_string& operator+=(initializer_list __il) { return append(__il); } +# | | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# | 1 error generated. +# `----------------------------- +# error: command failed with exit status: 1 +-- +''' +] + + +class ExecutedCommandBlock(BaseModel): + command : str + stdout : Optional[str] = None + stderr : Optional[str] = None + error_message : Optional[str] = None + +class LineLexer: + def __init__(self, lines, n): + self.lines = lines + self.n = n + + def peek(self): + if self.n < len(self.lines): + return self.lines[self.n] + else: + return None + + def ignore_blank(self, one = False): + while self.peek() and self.peek().strip() == '': + self.n += 1 + if one: + return + + + def untake(self): + self.n -= 1 + + def take_if(self, pred): + if pred(self.peek()): + return self.take() + else: + return None + + def take_if_prefix(self, prefix, remove_prefix = True): + if self.peek().startswith(prefix): + if remove_prefix: + return TryParseResult(True, self.take().value[len(prefix):]) + else: + return self.take() + else: + return TryParseResult(False, None) + + def take_while(self, pred): + while self.__bool__() and pred(self.peek()): + yield self.take() + + def take_matching(self, pattern, group = None): + r = re.compile(pattern) + m = r.match(self.peek()) + if m is None: + return TryParseResult(False, None) + self.take() + if group is None: + return TryParseResult(True, m.group()) + else: + return TryParseResult(True, m.group(group)) + + + def take(self): + if self.n < len(self.lines): + self.n += 1 + return TryParseResult(True, self.lines[self.n - 1]) + else: + raise IndexError("No more lines") + + def try_take_comment(self): + if self.peek().startswith('#'): + ln = self.take() + return TryParseResult(True, ln[1:].strip(), raw = ln) + else: + return TryParseResult(False, None) + + def try_take_comment_block(self): + + if self.peek().startswith('# '): + header = self.take()[1:].strip() + body = '' + while self.peek() and not self.peek().startswith('#'): + body += self.take() + return TryParseResult(True, NestedCommentBlock(header=header, body=body)) + else: + return TryParseResult(False, None) + + def try_parse_executed_command_block(self): + ln = self.peek() + if not ln.startswith('# executed command:'): + return TryParseResult(False, None) + else: + cmd = self.take().value + cmd = cmd[len('# executed command: '):] + blocks = {} + while self.peek() and self.peek().startswith('# .---'): + cmd_header = self.take_matching(r'# \.---([-]*)(?P
[^-]+)[-]+$', 'HEADER') + rich.print('command header', cmd_header) + lines = [ln.value[len('# | '):] for ln in self.take_while(lambda x: x.startswith('# |'))] + end_ln = self.take_if_prefix('# `---').raise_if_failed() + blocks[cmd_header.value] = '\n'.join(lines) + + res = self.take_matching('# error: (?P.*)', 'ERROR_MSG').raise_if_failed() + full = { + 'command': cmd, + 'stderr': blocks['command stderr'] if 'command stderr' in blocks else None, + 'stdout': blocks['command stdout'] if 'command stdout' in blocks else None, + 'error_message': res.value + } + return TryParseResult(True, ExecutedCommandBlock(**full)) + + def try_parse_command_block(self): + ln = self.peek() + if ln := self.take_matching(r'^# (?P[A-Z\s]+)\s*$', 'TITLE'): + title = ln.value + body = '' + while self.peek() and not self.peek().startswith('#'): + body += self.take().value + return TryParseResult(True, CommandBlock(header=title, body=body)) + else: + return TryParseResult(False, None) + + def try_parse_status_block(self): + if not self.peek().startswith('Exit Code:'): + return TryParseResult(False, None) + exit_code = self.take_matching(r'^Exit Code: (?P<EXIT_CODE>\d+)$', 'EXIT_CODE').raise_if_failed() + self.ignore_blank() + self.take_matching(r'^Command Output \(stdout\):$').raise_if_failed() + self.ignore_blank() + output = '' + while self.peek() and not self.peek().startswith('#'): + output += self.take() + return TryParseResult(True, StatusBlock(exit_code=exit_code.value, stdout=output)) + + def try_parse_block(self): + if sb := self.try_parse_status_block(): + return sb + elif eb := self.try_parse_executed_command_block(): + return eb + elif cb := self.try_parse_command_block(): + return cb + else: + return TryParseResult(False, None) + + + def take_while(self, pred): + while self.peek() and pred(self.peek()): + yield self.take() + + +import unittest + +class TestParse(unittest.TestCase): + def test_group_blocks(self): + cmd_block = ''' +# executed command: foo bar +# .---command stdout------------ +# | hello +# | world +# `----------------------------- +# .---command stderr------------ +# | error +# | message +# `----------------------------- +# error: command failed with exit status: 1 +'''.strip() + lexer = LineLexer(cmd_block.splitlines(), 0) + exe = lexer.try_parse_executed_command_block().raise_if_failed() + value = exe.value + self.assertTrue(exe) + self.assertEqual(value.command, 'foo bar') + rich.print(value) + self.assertEqual(value.stdout, 'hello\nworld') + + def test_take_command_block(self): + cmd_block = ''' +# EXECUTED +clang++ foo +# RAN +g++ bar +'''.strip() + lexer = LineLexer(cmd_block.splitlines(), 0) + b = lexer.try_parse_command_block().raise_if_failed() + self.assertTrue(b) + self.assertEqual(b.value.header, 'EXECUTED') + self.assertEqual(b.value.body, 'clang++ foo') + b = lexer.try_parse_command_block().raise_if_failed() + self.assertTrue(b) + self.assertEqual(b.value.header, 'RAN') + self.assertEqual(b.value.body, 'g++ bar') + + lexer = LineLexer(cmd_block.splitlines(), 0) + b = lexer.try_parse_block() + self.assertTrue(b) + self.assertEqual(b.value.header, 'EXECUTED') + + def test_clang_error_re(self): + test_diags = [ +''' +# | /home/eric/llvm-project/libcxx/test/std/example/fails-to-compile.pass.cpp:6:7: error: no viable overloaded '+=' +''', +''' +/home/eric/llvm-project/libcxx/test/std/example/fails-to-compile.pass.cpp:6:7: error: no viable overloaded '+=' +''' + ] + for t in test_diags: + t = t.strip() + m = ClangError.parse(t) + self.assertIsNotNone(m) + self.assertEqual(m.line, 6) + + +class LibcxxTestOutput(BaseModel): + Output : list[Union[ExecutedCommandBlock, CommandBlock, str]] = Field(default_factory=list) + diff --git a/src/llvmact/types/__init__.py b/src/llvmact/types/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/llvmact/types/github.py b/src/llvmact/types/github.py new file mode 100644 index 0000000..c628655 --- /dev/null +++ b/src/llvmact/types/github.py @@ -0,0 +1,26 @@ +from pydantic import BaseModel, Field +from typing import Literal, Any, Union, Optional + +class Annotation(BaseModel): + path: str + start_line: int + end_line: int + annotation_level: Literal["notice", "warning", "failure"] + message: str + title: str + raw_details: str + +class CheckRun(BaseModel): + name: str + head_sha: str + status: Literal["queued", "in_progress", "completed"] + conclusion: Literal["success", "failure", "neutral", "cancelled", "timed_out", "action_required"] + output: dict[str, Any] = Field(default_factory=dict) + actions: list[dict[str, Any]] = Field(default_factory=list) + check_suite: dict[str, Any] = Field(default_factory=dict) + external_id: str + started_at: str + completed_at: str + output: dict[str, Any] = Field(default_factory=dict) + annotations: list[Annotation] = Field(default_factory=list) + pull_requests: list[dict[str, Any]] = Field(default_factory=list) diff --git a/src/llvmact/types/llvm.py b/src/llvmact/types/llvm.py new file mode 100644 index 0000000..0a6c358 --- /dev/null +++ b/src/llvmact/types/llvm.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel, Field +from typing import Literal, Any, Union, Optional + + +class TestResult(BaseModel): + code: Literal["PASS", "FAIL", "SKIP", "XPASS"] + elapsed: float + metrics: dict[str, Any] = Field(default_factory=dict) + name: str + output: str + +class LITTestResults(BaseModel): + version: tuple[int, int, int] = Field(alias="__version__") + elapsed: float + tests: list[TestResult] = Field(default_factory=list)