code: def build_extension(
      name,
      path_i,
      outdir,
      builddir=None,
      includes=None,
      defines=None,
      libpaths=None,
      libs=None,
      optimise=True,
      debug=False,
      compiler_extra="",
      linker_extra="",
      swig="swig",
      cpp=True,
      prerequisites_swig=None,
      prerequisites_compile=None,
      prerequisites_link=None,
      infer_swig_includes=True,
  ):
      """
      Builds a Python extension module using SWIG. Works on Windows, Linux, MacOS
      and OpenBSD.
  
      On Unix, sets rpath when linking shared libraries.
  
      Args:
          name:
              Name of generated extension module.
          path_i:
              Path of input SWIG `.i` file. Internally we use swig to generate a
              corresponding `.c` or `.cpp` file.
          outdir:
              Output directory for generated files:
  
                  * `{outdir}/{name}.py`
                  * `{outdir}/_{name}.so`     # Unix
                  * `{outdir}/_{name}.*.pyd`  # Windows
              We return the leafname of the `.so` or `.pyd` file.
          builddir:
              Where to put intermediate files, for example the .cpp file
              generated by swig and `.d` dependency files. Default is `outdir`.
          includes:
              A string, or a sequence of extra include directories to be prefixed
              with `-I`.
          defines:
              A string, or a sequence of extra preprocessor defines to be
              prefixed with `-D`.
          libpaths
              A string, or a sequence of library paths to be prefixed with
              `/LIBPATH:` on Windows or `-L` on Unix.
          libs
              A string, or a sequence of library names to be prefixed with `-l`.
          optimise:
              Whether to use compiler optimisations.
          debug:
              Whether to build with debug symbols.
          compiler_extra:
              Extra compiler flags.
          linker_extra:
              Extra linker flags.
          swig:
              Base swig command.
          cpp:
              If true we tell SWIG to generate C++ code instead of C.
          prerequisites_swig:
          prerequisites_compile:
          prerequisites_link:
  
              [These are mainly for use on Windows. On other systems we
              automatically generate dynamic dependencies using swig/compile/link
              commands' `-MD` and `-MF` args.]
  
              Sequences of extra input files/directories that should force
              running of swig, compile or link commands if they are newer than
              any existing generated SWIG `.i` file, compiled object file or
              shared library file.
  
              If present, the first occurrence of `True` or `False` forces re-run
              or no re-run. Any occurrence of None is ignored. If an item is a
              directory path we look for newest file within the directory tree.
  
              If not a sequence, we convert into a single-item list.
  
              prerequisites_swig
  
                  We use swig's -MD and -MF args to generate dynamic dependencies
                  automatically, so this is not usually required.
  
              prerequisites_compile
              prerequisites_link
  
                  On non-Windows we use cc's -MF and -MF args to generate dynamic
                  dependencies so this is not usually required.
          infer_swig_includes:
              If true, we extract `-I<path>` and `-I <path>` args from
              `compile_extra` (also `/I` on windows) and use them with swig so
              that it can see the same header files as C/C++. This is useful
              when using enviromment variables such as `CC` and `CXX` to set
              `compile_extra.
  
      Returns the leafname of the generated library file within `outdir`, e.g.
      `_{name}.so` on Unix or `_{name}.cp311-win_amd64.pyd` on Windows.
      """
      if builddir is None:
          builddir = outdir
      includes_text = _flags(includes, "-I")
      defines_text = _flags(defines, "-D")
      libpaths_text = (
          _flags(libpaths, "/LIBPATH:", '"')
          if windows()
          else _flags(libpaths, "-L")
      )
      libs_text = _flags(libs, "-l")
      path_cpp = f"{builddir}/{os.path.basename(path_i)}"
      path_cpp += ".cpp" if cpp else ".c"
      os.makedirs(outdir, exist_ok=True)
  
      # Run SWIG.
  
      if infer_swig_includes:
          # Extract include flags from `compiler_extra`.
          swig_includes_extra = ""
          compiler_extra_items = compiler_extra.split()
          i = 0
          while i < len(compiler_extra_items):
              item = compiler_extra_items[i]
              # Swig doesn't seem to like a space after `I`.
              if item == "-I" or (windows() and item == "/I"):
                  swig_includes_extra += (
                      f" -I{compiler_extra_items[i+1]}"
                  )
                  i += 1
              elif item.startswith("-I") or (
                  windows() and item.startswith("/I")
              ):
                  swig_includes_extra += (
                      f" -I{compiler_extra_items[i][2:]}"
                  )
              i += 1
          swig_includes_extra = swig_includes_extra.strip()
      deps_path = f"{path_cpp}.d"
      prerequisites_swig2 = _get_prerequisites(deps_path)
      run_if(
          f"""
              {swig}
                  -Wall
                  {"-c++" if cpp else ""}
                  -python
                  -module {name}
                  -outdir {outdir}
                  -o {path_cpp}
                  -MD -MF {deps_path}
                  {includes_text}
                  {swig_includes_extra}
                  {path_i}
              """,
          path_cpp,
          path_i,
          prerequisites_swig,
          prerequisites_swig2,
      )
  
      path_so_leaf = f"_{name}{_so_suffix()}"
      path_so = f"{outdir}/{path_so_leaf}"
  
      if windows():
          path_obj = f"{path_so}.obj"
  
          permissive = "/permissive-"
          EHsc = "/EHsc"
          T = "/Tp" if cpp else "/Tc"
          optimise2 = "/DNDEBUG /O2" if optimise else "/D_DEBUG"
          debug2 = ""
          if debug:
              debug2 = "/Zi"  # Generate .pdb.
              # debug2 = '/Z7'    # Embded debug info in .obj files.
  
          # As of 2023-08-23, it looks like VS tools create slightly
          # .dll's each time, even with identical inputs.
          #
          # Some info about this is at:
          # https://nikhilism.com/post/2020/windows-deterministic-builds/.
          # E.g. an undocumented linker flag `/Brepro`.
          #
  
          command, pythonflags = base_compiler(cpp=cpp)
          command = f"""
                  {command}
                      # General:
                      /c                          # Compiles without linking.
                      {EHsc}                      # Enable "Standard C++ exception handling".
  
                      #/MD                         # Creates a multithreaded DLL using MSVCRT.lib.
                      {'/MDd' if debug else '/MD'}
  
                      # Input/output files:
                      {T}{path_cpp}               # /Tp specifies C++ source file.
                      /Fo{path_obj}               # Output file.
  
                      # Include paths:
                      {includes_text}
                      {pythonflags.includes}      # Include path for Python headers.
  
                      # Code generation:
                      {optimise2}
                      {debug2}
                      {permissive}                # Set standard-conformance mode.
  
                      # Diagnostics:
                      #/FC                         # Display full path of source code files passed to cl.exe in diagnostic text.
                      /W3                         # Sets which warning level to output. /W3 is IDE default.
                      /diagnostics:caret          # Controls the format of diagnostic messages.
                      /nologo                     #
  
                      {defines_text}
                      {compiler_extra}
                  """
          run_if(command, path_obj, path_cpp, prerequisites_compile)
  
          command, pythonflags = base_linker(cpp=cpp)
          debug2 = "/DEBUG" if debug else ""
          base, _ = os.path.splitext(path_so_leaf)
          command = f"""
                  {command}
                      /DLL                    # Builds a DLL.
                      /EXPORT:PyInit__{name}  # Exports a function.
                      /IMPLIB:{base}.lib      # Overrides the default import library name.
                      {libpaths_text}
                      {pythonflags.ldflags}
                      /OUT:{path_so}          # Specifies the output file name.
                      {debug2}
                      /nologo
                      {libs_text}
                      {path_obj}
                      {linker_extra}
                  """
          run_if(command, path_so, path_obj, prerequisites_link)
  
      else:
          # Not Windows.
          #
          command, pythonflags = base_compiler(cpp=cpp)
  
          # setuptools on Linux seems to use slightly different compile flags:
          #
          # -fwrapv -O3 -Wall -O2 -g0 -DPY_CALL_TRAMPOLINE
          #
  
          general_flags = ""
          if debug:
              general_flags += " -g"
          if optimise:
              general_flags += " -O2 -DNDEBUG"
  
          if darwin():
              # MacOS's linker does not like `-z origin`.
              rpath_flag = "-Wl,-rpath,@loader_path/"
  
              # Avoid `Undefined symbols for ... "_PyArg_UnpackTuple" ...'.
              general_flags += " -undefined dynamic_lookup"
          elif pyodide():
              # Setting `-Wl,-rpath,'$ORIGIN',-z,origin` gives:
              #   emcc: warning: ignoring unsupported linker flag: `-rpath` [-Wlinkflags]
              #   wasm-ld: error: unknown -z value: origin
              #
              log0(
                  f"pyodide: PEP-3149 suffix untested, so omitting. {_so_suffix()=}."
              )
              path_so_leaf = f"_{name}.so"
              path_so = f"{outdir}/{path_so_leaf}"
  
              rpath_flag = ""
          else:
              rpath_flag = "-Wl,-rpath,'$ORIGIN',-z,origin"
          path_so = f"{outdir}/{path_so_leaf}"
          # Fun fact - on Linux, if the -L and -l options are before '{path_cpp}'
          # they seem to be ignored...
          #
          prerequisites = list()
  
          if pyodide():
              # Looks like pyodide's `cc` can't compile and link in one invocation.
              prerequisites_compile_path = f"{path_cpp}.o.d"
              prerequisites += _get_prerequisites(
                  prerequisites_compile_path
              )
              command = f"""
                      {command}
                          -fPIC
                          {general_flags.strip()}
                          {pythonflags.includes}
                          {includes_text}
                          {defines_text}
                          -MD -MF {prerequisites_compile_path}
                          -c {path_cpp}
                          -o {path_cpp}.o
                          {compiler_extra}
                      """
              prerequisites_link_path = f"{path_cpp}.o.d"
              prerequisites += _get_prerequisites(
                  prerequisites_link_path
              )
              ld, _ = base_linker(cpp=cpp)
              command += f"""
                      && {ld}
                          {path_cpp}.o
                          -o {path_so}
                          -MD -MF {prerequisites_link_path}
                          {rpath_flag}
                          {libpaths_text}
                          {libs_text}
                          {linker_extra}
                          {pythonflags.ldflags}
                      """
          else:
              # We use compiler to compile and link in one command.
              prerequisites_path = f"{path_so}.d"
              prerequisites = _get_prerequisites(prerequisites_path)
  
              command = f"""
                      {command}
                          -fPIC
                          -shared
                          {general_flags.strip()}
                          {pythonflags.includes}
                          {includes_text}
                          {defines_text}
                          {path_cpp}
                          -MD -MF {prerequisites_path}
                          -o {path_so}
                          {compiler_extra}
                          {libpaths_text}
                          {linker_extra}
                          {pythonflags.ldflags}
                          {libs_text}
                          {rpath_flag}
                      """
          command_was_run = run_if(
              command,
              path_so,
              path_cpp,
              prerequisites_compile,
              prerequisites_link,
              prerequisites,
          )
  
          if command_was_run and darwin():
              # We need to patch up references to shared libraries in `libs`.
              sublibraries = list()
              for lib in libs:
                  for libpath in libpaths:
                      found = list()
                      for suffix in ".so", ".dylib":
                          path = f"{libpath}/lib{os.path.basename(lib)}{suffix}"
                          if os.path.exists(path):
                              found.append(path)
                      if found:
                          assert (
                              len(found) == 1
                          ), f"More than one file matches lib={lib!r}: {found}"
                          sublibraries.append(found[0])
                          break
                  else:
                      log2(
                          f"Warning: can not find path of lib={lib!r} in libpaths={libpaths}"
                      )
              macos_patch(path_so, *sublibraries)
  
          # run(f'ls -l {path_so}', check=0)
          # run(f'file {path_so}', check=0)
  
      return path_so_leaf
  
imports: import base64
  import glob
  import hashlib
  import inspect
  import io
  import os
  import platform
  import re
  import shutil
  import site
  import setuptools
  import subprocess
  import sys
  import sysconfig
  import tarfile
  import textwrap
  import time
  import zipfile
  import wdev
label_desc: Use image node_id 1 for calling a global function with example usage: build_extension(name, path_i, outdir, builddir, includes, defines, libpaths, libs, optimise, debug, compiler_extra, linker_extra, swig, cpp, prerequisites_swig, prerequisites_compile, prerequisites_link, infer_swig_includes) and returns: path_so_leaf