CTinker - C/C++ Project Introspection and Augmentation Tool
C Tinker, pronounced see-tinker (or humorously "stinker", as suggested by Chuck Ocheret) allows you to get in the middle of the build process of a make/Ninja-style project and augment the compilation and linking as well as extract and redirect artifacts using policies you can't implement otherwise even with LDFLAGS/CFLAGS magic.
Problem
More formally the problem CTinker solves can be stated as follows:
I need to get in the middle of a build process of a project I can know intimately but do not control and that I have no intention of maintaining a fork/patches for, or for which I need to obtain runtime dynamic control of the build process.
Solution
Overview
CTinker is capable of getting in the middle of virtually any build process by:
- Starting in the
supervisormode. - Creating a temporary directory full of toolkit-specific (e.g. for LLVM Clang it's
clang,aretc) symlinks referring back toCTinkerexecutable. - Setting up environ and a local socket to communicate with the
workers. - Invoking the build process as specified by the user.
- Being invoked for each tool invocation in a
workermode (based on environmental variables), communicating with thesupervisor, sending command-line arguments to thesupervisorprocess and then invoking the tool itself. - If specified, invoking
scriptinghandlers before and after the build as a whole (in thesupervisor) and before and after each intercepted tool invocation (in theworker).
As a further illustration, if the original process invocation chain for a sample build is as follows:
make => clang => lld, => make => clang, => clang => lld
then the same build instrumented with CTinker will produce the following process invocation chain:
ctinker => make => ctinker-clang => clang => ctinker-lld => lld, => make => ctinker-clang => clang, => ctinker-clang => clang => ctinker-lld => lld
Scripting
Scripting is the most powerful part of CTinker that provides an ability to really change how build functions
at runtime. It is implemented via a visitor pattern, invoking functions specified in the user-supplied script:
def ctinker_start(env: Dict[str, str], work_dir: Path):
"""Invoked by CTinker `supervisor` prior to the main build process
Changes to the `env` dictionary propagate to the main build process.
"""
pass
def ctinker_finish(env: Dict[str, str], work_dir: Path, tool_calls: List[Tuple[Any]], return_code: int):
"""Invoked by CTinker `supervisor` after the main build process exits
`tool_calls` is a `list` of `tuple`s of `(tool, tool_args, return_code, cwd, script_result)`, where `script_result`
is the value returned by `ctinker_after_tool`.
"""
pass
def ctinker_before_tool(env: Dict[str, str], tool: str, tool_args: List[str], work_dir: Path, cwd: Path):
"""Invoked by CTinker `worker` prior to the tool process
Changes to the `env` dictionary propagate to the tool process.
Changes to the `tool_args` list propagate to the tool process.
"""
pass
def ctinker_after_tool(env: Dict[str, str], tool: str, tool_args: List[str], work_dir: Path, cwd: Path,
return_code: int) -> Any:
"""Invoked by CTinker `worker` after the tool process exits
Returned value, **if truthy**, will be stored and will appear
as the last entry in the `tool_calls` passed to `ctinker_finish`
"""
pass
It is guaranteed that ctinker_start - ctinker_finish and ctinker_before_tool - ctinker_after_tool pairs will
be executed in the same supervisor and worker processes respectively and therefore you can pass values between
the start/finish and before/after functions (for example by a global or within the same instance of an object).
Help
$ ctinker --help
usage: ctinker [-h] [-s SCRIPT] [-o OUT] [-f {text,pickle}] [-p PATHS]
[-w WORK_DIR] -t {clang} [-l [{clang,__default}]]
[-L LINKER_TOOL_OVERRIDE]
[--linker-flags-name LINKER_FLAGS_NAME]
...
CTinker project introspection and augmentation tool
positional arguments:
command build command to execute
optional arguments:
-h, --help show this help message and exit
-s SCRIPT, --script SCRIPT
a Python script containing visitor-style hooks
(default: None)
-o OUT, --out OUT a path to a file where all tools, their arguments and
exit codes will be recorded (default: None)
-f {text,pickle}, --format {text,pickle}
the format of the output file (default: text)
-p PATHS, --path PATHS
prepend a leading PATH entry to be inherited by the
invoked command (default: None)
-w WORK_DIR, --work-dir WORK_DIR
sets a work directory to be something other than
current working directory (default: )
-t {clang}, --toolkit {clang}
enable specific compilation interception modes
(default: None)
-l [{clang,__default}], --linker-intercept [{clang,__default}]
intercept linker with --linker-flags-name env var
using the specified toolkit (default: None)
-L LINKER_TOOL_OVERRIDE, --linker-tool-override LINKER_TOOL_OVERRIDE
specify linker tool name directly (may not work if no
toolkit provides it) (default: None)
--linker-flags-name LINKER_FLAGS_NAME
specify linker environmental variable (default:
LDFLAGS)
Example
TBW
Troubleshooting
- Printing to
sys.stdoutfrom theworkeris dangerous as the stdout is often interpreted by the invoking tool which can lead to a crash in the tool expecting certain data format.print("debug!", file=sys.stderr)is generally safe.
Release files for ctinker 0.0.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ctinker-0.0.4.tar.gz | 16.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ctinker-0.0.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.1 kB
Release files / ctinker-0.0.4.tar.gz
| Download URL | ctinker-0.0.4.tar.gz |
|---|---|
| Size | 16.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f4625dcf840176d4498eb821118293fdf105a61526a36d02104a019b1239810a
|
|
BLAKE2b-256 checksum How to use checksums |
a3634e60d5bd62df15e612bcd744b306e1445da63da8e5bf8fa8ce4ce34bcb0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.1.3 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.8.2
|
Release files / ctinker-0.0.4-py3-none-any.whl
| Download URL | ctinker-0.0.4-py3-none-any.whl |
|---|---|
| Size | 14.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cf21f67aa437d664aa2c83471d5e369805a62a0d2727dda1006691cca14136a7
|
|
BLAKE2b-256 checksum How to use checksums |
9afd18b77a6c2a7315b5d3037f8de11e20479f6c0fac72432dcb9f7ab0cce3f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.1.3 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.8.2
|