Package: ctinker
CTinker - C/C++ Project Introspection and Augmentation ToolC Tinker, pronouncedsee-tinker(or humorously "stinker", as suggested byChuck 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.ProblemMore formally the problemCTinkersolves 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.SolutionOverviewCTinkeris capable of getting in the middle of virtually any build process by:Starting in thesupervisormode.Creating a temporary directory full of toolkit-specific (e.g. for LLVM Clang it'sclang,aretc)
symlinks referring back toCTinkerexecutable.Setting up environ and a local socket to communicate with theworkers.Invoking the build process as specified by the user.Being invoked for each tool invocation in aworkermode (based on environmental variables),
communicating with thesupervisor, sending command-line arguments to thesupervisorprocess and then
invoking the tool itself.If specified, invokingscriptinghandlers 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 => lldthen 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 => lldScriptingScripting is the most powerful part ofCTinkerthat 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:defctinker_start(env:Dict[str,str],work_dir:Path):"""Invoked by CTinker `supervisor` prior to the main build processChanges to the `env` dictionary propagate to the main build process."""passdefctinker_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`."""passdefctinker_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 processChanges to the `env` dictionary propagate to the tool process.Changes to the `tool_args` list propagate to the tool process."""passdefctinker_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 exitsReturned value, **if truthy**, will be stored and will appearas the last entry in the `tool_calls` passed to `ctinker_finish`"""passIt is guaranteed thatctinker_start-ctinker_finishandctinker_before_tool-ctinker_after_toolpairs will
be executed in the samesupervisorandworkerprocessesrespectivelyand 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][-sSCRIPT][-oOUT][-f{text,pickle}][-pPATHS][-wWORK_DIR]-t{clang}[-l[{clang,__default}]][-LLINKER_TOOL_OVERRIDE][--linker-flags-nameLINKER_FLAGS_NAME]...

CTinkerprojectintrospectionandaugmentationtool

positionalarguments:commandbuildcommandtoexecute

optionalarguments:-h,--helpshowthishelpmessageandexit-sSCRIPT,--scriptSCRIPTaPythonscriptcontainingvisitor-stylehooks(default:None)-oOUT,--outOUTapathtoafilewherealltools,theirargumentsandexitcodeswillberecorded(default:None)-f{text,pickle},--format{text,pickle}theformatoftheoutputfile(default:text)-pPATHS,--pathPATHSprependaleadingPATHentrytobeinheritedbytheinvokedcommand(default:None)-wWORK_DIR,--work-dirWORK_DIRsetsaworkdirectorytobesomethingotherthancurrentworkingdirectory(default:)-t{clang},--toolkit{clang}enablespecificcompilationinterceptionmodes(default:None)-l[{clang,__default}],--linker-intercept[{clang,__default}]interceptlinkerwith--linker-flags-nameenvvarusingthespecifiedtoolkit(default:None)-LLINKER_TOOL_OVERRIDE,--linker-tool-overrideLINKER_TOOL_OVERRIDEspecifylinkertoolnamedirectly(maynotworkifnotoolkitprovidesit)(default:None)--linker-flags-nameLINKER_FLAGS_NAMEspecifylinkerenvironmentalvariable(default:LDFLAGS)ExampleTBWTroubleshootingPrinting tosys.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.