[llvm] [polly] [Buildbot][Polly] Move polly-x86_64-linux-test-suite build instructions into main repository (PR #166809)
Jan Patrick Lehr via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 17 00:25:02 PST 2025
================
@@ -0,0 +1,471 @@
+import argparse
+import filecmp
+import os
+import pathlib
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+import traceback
+import platform
+import multiprocessing
+from contextlib import contextmanager
+
+_SHQUOTE_WINDOWS_ESCAPEDCHARS = re.compile(r'(["\\])')
+_SHQUOTE_WINDOWS_QUOTEDCHARS = re.compile("[ \t\n]")
+
+
+def _shquote_windows(txt):
+ """shlex.quote for Windows cmd.exe"""
+ txt = txt.replace("%", "%%")
+ quoted = re.sub(_SHQUOTE_WINDOWS_ESCAPEDCHARS, r"\\\1", txt)
+ if len(quoted) == len(txt) and not _SHQUOTE_WINDOWS_QUOTEDCHARS.search(txt):
+ return txt
+ else:
+ return '"' + quoted + '"'
+
+
+def shjoin(args):
+ """Convert a list of shell arguments to an appropriately quoted string."""
+ if os.name in set(("nt", "os2", "ce")):
+ return " ".join(map(_shquote_windows, args))
+ else:
+ return shlex.join(args)
+
+
+def report(msg):
+ """Emit a message to the build log. Appears in red font. Lines surrounded by @@@ may be interpreted as meta-instructions."""
+ print(msg, file=sys.stderr, flush=True)
+
+
+def report_prog_version(name, cmd):
+ try:
+ p = subprocess.run(cmd, check=True, capture_output=True, text=True)
+ outlines = p.stdout.strip().splitlines()
+ report_list(name, outlines[0])
+ except BaseException:
+ pass
+
+
+def report_list(category, *items):
+ items = list(items)
+ filtered = []
+
+ while items:
+ item = items.pop()
+ match item:
+ case tuple() | list():
+ items += item
+ continue
+ case None:
+ continue
+ case _:
+ item = str(item).strip()
+ if not item:
+ continue
+ if item in filtered:
+ continue
+ filtered.append(item)
+ category += ":"
+ report(f"{category:<9}{', '.join(reversed( filtered))}")
+
+
+def report_platform():
+ report_list(
+ "CPU",
+ platform.machine(),
+ platform.architecture()[0],
+ platform.processor(),
+ f"{multiprocessing.cpu_count()} native threads",
+ )
+ try:
+ releaseinfo = platform.freedesktop_os_release()
+ except BaseException:
+ releaseinfo = dict()
+ report_list(
+ "OS",
+ platform.system(),
+ platform.architecture()[1],
+ platform.platform(),
+ releaseinfo.get("PRETTY_NAME"),
+ )
+ report_list("Python", platform.python_implementation(), platform.python_version())
+
+ report_prog_version("CMake", ["cmake", "--version"])
+ report_prog_version("Ninja", ["ninja", "--version"])
+ report_prog_version("Sphinx", ["sphinx-build", "--version"])
+ report_prog_version("Doxygen", ["doxygen", "--version"])
+
+ report_prog_version("gcc", ["gcc", "--version"])
+ report_prog_version("ld", ["ld", "--version"])
+
+ report_prog_version("LLVM", ["llvm-config", "--version"])
+ report_prog_version("Clang", ["clang", "--version"])
+ report_prog_version("LLD", ["ld.lld", "--version"])
+
+
+def run_command(cmd, shell=False, **kwargs):
+ """Report which command is being run, then execute it using subprocess.check_call."""
+ report(f"Running: {cmd if shell else shjoin(cmd)}")
+ sys.stderr.flush()
+ subprocess.check_call(cmd, shell=shell, **kwargs)
+
+
+def _remove_readonly(func, path, _):
+ """Clear the readonly bit and reattempt the removal."""
+ os.chmod(path, os.stat.S_IWRITE)
+ func(path)
+
+
+def rmtree(path):
+ """Remove directory path and all its subdirectories. Includes a workaround
+ for Windows where shutil.rmtree errors on read-only files.
+
+ Taken from official Pythons docs
+ https://docs.python.org/3/library/shutil.html#rmtree-example
+ """
+ shutil.rmtree(path, onexc=_remove_readonly)
+
+
+def checkout(giturl, sourcepath):
+ """
+ Use git to checkout the remote repository giturl at local directory sourcepath.
+
+ If the repository already exists, clear all local changes and check out the latest main branch.
+ """
+ if not os.path.exists(sourcepath):
+ run_command(["git", "clone", giturl, sourcepath])
+
+ # Reset repository state no matter what there was before
+ run_command(["git", "-C", sourcepath, "stash", "--all"])
+ run_command(["git", "-C", sourcepath, "stash", "clear"])
+
+ # Fetch and checkout the newest
+ run_command(["git", "-C", sourcepath, "fetch", "origin"])
+ run_command(["git", "-C", sourcepath, "checkout", "origin/main", "--detach"])
+
+
+ at contextmanager
+def step(step_name, halt_on_fail=False):
+ """Report a new build step being started.
+
+ Use like this::
+ with step("greet-step"):
+ report("Hello World!")
+ """
+ # Barrier to separate stdio output for the the previous step
+ sys.stderr.flush()
+ sys.stdout.flush()
+
+ report(f"@@@BUILD_STEP {step_name}@@@")
+ if halt_on_fail:
+ report("@@@HALT_ON_FAILURE@@@")
+ try:
+ yield
+ except Exception as e:
+ if isinstance(e, subprocess.CalledProcessError):
+ report(f"{shjoin(e.cmd)} exited with return code {e.returncode}.")
+ report("@@@STEP_FAILURE@@@")
+ else:
+ traceback.print_exc()
+ report("@@@STEP_EXCEPTION@@@")
+ if halt_on_fail:
+ # Do not continue with the next steps, but allow except/finally blocks to execute
+ raise e
+
+
+class Worker:
+ """Helper class to keep context in a worker.run() environment"""
+
+ def __init__(self, args, clean, clobber, workdir, jobs, cachefile, llvmsrcroot):
+ self.args = args
+ self.clean = clean
+ self.clobber = clobber
+ self.workdir = workdir
+ self.jobs = jobs
+ self.cachefile = cachefile
+ self.llvmsrcroot = llvmsrcroot
+
+ def in_llvmsrc(self, path):
+ """Convert a path in the llvm-project source checkout to an absolute path"""
+ return os.path.join(self.llvmsrcroot, path)
+
+ def in_workdir(self, path):
+ """Convert a path in the workdir to an absolute path"""
+ return os.path.join(self.workdir, path)
+
+ def run_ninja(
+ self, targets: list = [], *, builddir, ccache_stats: bool = False, **kwargs
+ ):
+ """Run ninja in builddir. If self.jobs is set, automatically adds an -j option to set the number of parallel jobs.
+
+ Parameters
+ ----------
+ targets : list
+ List of build targets; build the default target 'all' if list is empty
+ builddir
+ Directory of the build.ninja file
+ ccache_stats : bool
+ If true, also emit ccache statistics when finishing the build
+ """
+ cmd = ["ninja"]
+ if builddir is not None:
+ cmd += ["-C", builddir]
+ cmd += targets
+ if self.jobs:
+ cmd.append(f"-j{self.jobs}")
+ if ccache_stats:
+ run_command(["ccache", "-z"])
+ try:
+ run_command(cmd, **kwargs)
+ finally:
+ # TODO: Pipe to stderr to separate from build log itself
+ run_command(["ccache", "-sv"])
+ else:
+ run_command(cmd, **kwargs)
+
+ @contextmanager
+ def step(self, step_name, halt_on_fail=False):
+ """Convenience wrapper for step()"""
+ with step(step_name, halt_on_fail=halt_on_fail) as s:
+ yield s
+
+ def report(self, msg):
+ """Convenience wrapper for report()"""
+ report(msg)
+
+ def run_command(self, *args, **kwargs):
+ """Convenience wrapper for run_command()"""
+ return run_command(*args, **kwargs)
+
+ def rmtree(self, *args, **kwargs):
+ """Convenience wrapper for rmtree()"""
+ return rmtree(*args, *kwargs)
+
+ def checkout(self, giturl, sourcepath):
+ """Convenience wrapper for checkout()"""
+ return checkout(giturl, sourcepath)
+
+
+def convert_bool(v):
+ """Convert input to bool type
+
+ Use to convert the value of bool environment variables. Specifically, the
+ buildbot master sets 'false' to build properties, which by default Python
+ would interpret as true-ish.
+ """
+ match v:
+ case None:
+ return False
+ case bool(b):
+ return b
+ case str(s):
+ return not s.strip().upper() in ["", "0", "N", "NO", "FALSE", "OFF"]
+ case _:
+ return bool(v)
+
+
+def relative_if_possible(path, relative_to):
+ """Like os.path.relpath, but does not fail if path is not a parent of relative_to; keeps the original path in that case"""
+ path = os.path.normpath(path)
+ if not os.path.isabs(path):
+ # Path is already relative (assumed to relative_to)
+ return path
+ try:
+ result = os.path.relpath(path, start=relative_to)
+ return result if result else path
+ except ValueError:
+ return path
+
+
+ at contextmanager
+def run(
+ scriptpath,
+ llvmsrcroot,
+ parser=None,
+ clobberpaths=[],
+ workerjobs=None,
+ always_clobber=False,
+):
+ """
+ Runs the boilerplate for a ScriptedBuilder buildbot. It is not necessary to
+ use this function (one can also all run_command() etc. directly), but allows
+ for some more flexibility and safety checks. Arguments passed to this
+ function represent the worker configuration.
+
+ We use the term 'clean' for resetting the worker to an empty state. This
+ involves deleting ${prefix}/llvm.src as well as ${prefix}/build.
+ The term 'clobber' means deleting build artifacts, but not already
+ downloaded git repositories. Build artifacts including build- and
+ install-directories, but not source directories. Changes in the llvm.src
+ directory will be reset before the next build anyway. Clobber is necessary
+ if the build instructions change. Otherwise, we try an incremental build.
----------------
jplehr wrote:
Should this default to always clobber? The instructions on the LLVM documentation "How to setup a buildbot" strongly advise against incremental builds and rely on ccache instead. Should the default behavior here reflect that?
https://github.com/llvm/llvm-project/pull/166809
More information about the llvm-commits
mailing list