{ "info": { "author": "Ilya Shchepetkov", "author_email": "shchepetkov@ispras.ru", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: Apache Software License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython" ], "description": ".. image:: https://travis-ci.org/17451k/clade.svg?branch=master\n :target: https://travis-ci.org/17451k/clade\n :alt: Build status\n.. image:: https://coveralls.io/repos/github/17451k/clade/badge.svg?branch=master\n :target: https://coveralls.io/github/17451k/clade?branch=master\n :alt: Code coverage information\n.. image:: https://img.shields.io/pypi/pyversions/clade.svg\n :target: https://pypi.org/project/clade/\n :alt: Supported versions of Python\n.. image:: https://img.shields.io/pypi/v/clade.svg\n :target: https://pypi.org/project/clade\n :alt: PyPi package version\n\n\n.. contents::\n :local:\n\n============\nIntroduction\n============\n\nClade is a tool for intercepting build commands (stuff like compilation,\nlinking, mv, rm, and all other commands that are executed during build).\nIntercepted commands can be parsed (to search for input and output files,\nand options) and then used for various purposes:\n\n- generating `compilation database`_;\n- obtaining information about dependencies between source and object files;\n- obtaining information about the source code (source code querying);\n- generating function call graph;\n- running software verification tools;\n- visualization of all collected information;\n- *and for much more*.\n\n.. _compilation database: https://clang.llvm.org/docs/JSONCompilationDatabase.html\n\nThe interception of build commands is independent of the project type\nand used programming languages.\nHowever, all other functionality available in Clade IS dependent.\nCurrently only C projects are supported, but other languages and additional\nfunctionality can be supported through the built-in *extension mechanism*.\n\n=============\nPrerequisites\n=============\n\nAn important part of Clade - a build commands intercepting library -\nis written in C and it needs to be compiled before use.\nIt will be performed automatically at the installation stage, but you will\nneed to install some prerequisites beforehand:\n\n- Python 3 (>=3.4)\n- pip (Python package manager)\n- cmake (>=3.3)\n\n*Linux only*:\n\n- make\n- C **and** C++ compiler (gcc or clang)\n- python3-dev (Ubuntu) or python3-devel (openSUSE) package\n- gcc-multilib (Ubuntu) or gcc-32bit (openSUSE) package\n to intercept build commands of projects leveraging multilib capabilities\n\n*Windows only*:\n\n- Microsoft Visual C++ Build Tools\n\nOptional dependencies:\n\n- For obtaining information about the C code you will need CIF_\n installed. CIF is an interface to Aspectator_ which in turn is a GCC\n based tool that implements aspect-oriented programming for the C programming\n language. You may download compiled CIF on `CIF releases`_ page.\n- Graphviz for some visualization capabilities\n\n.. _CIF: https://github.com/17451k/cif\n.. _Aspectator: https://github.com/17451k/aspectator\n.. _CIF releases: https://github.com/17451k/cif/releases\n\nClade works on Linux, macOS and partially on Windows.\n\n=====================\nHardware requirements\n=====================\n\nIf you want to run Clade on a large project, like the Linux kernel,\nyou will need at least 16GB of RAM and 100GB of free disk space\nfor temporary files. The size of generated data will be approximately\n10GB, so the space used for temporary files will be freed at the end.\nAlso several CPU cores are recommended, since in some cases Clade takes\ntwice as long time than a typical build process.\n\n============\nInstallation\n============\n\nTo install the latest stable version just run the following command\n(you may need to replace pip by pip3):\n\n.. code-block:: bash\n\n $ pip install clade\n\nFor development purposes you may install Clade in \"editable\" mode\ndirectly from the repository (clone it on your computer beforehand):\n\n.. code-block:: bash\n\n $ pip install -e .\n\nYou can check that Clade works as expected on your machine by running\nthe test suite from the repository (doesn't work on Windows yet):\n\n.. code-block:: bash\n\n $ pytest\n\n==========\nHow to use\n==========\n\nThe simplest way to start using Clade is to run the following command:\n\n.. code-block:: bash\n\n $ clade make\n\nwhere *make* should be replaced by your project build command. Clade will\nexecuite build, intercept build commands, parse them and generate a lot of data\nabout build process and source files. The following sections explain formats\nof the generated data, as well as some other things.\n\nAll functionality is available both as command-line scripts and\nas Python modules that you can import and use, so the following\nexamples will include both use cases.\n\nBuild command intercepting\n--------------------------\n\nIntercepting of build commands is quite easy: all you need is to\nwrap your main build command like this:\n\n.. code-block:: bash\n\n $ clade -i make\n\nwhere *make* should be replaced by your project build command.\nThe output file called *cmds.txt* will be stored in the directory named *clade*\nand will contain all intercepted commands, one per line.\n\nNote that *clade -i* only intercepts build commands and does not process\nthem in any way.\n\nYou can change the path to to the file where intercepted commands will be\nsaved using --cmds option:\n\n.. code-block:: bash\n\n $ clade -i --cmds /work/cmds.txt make\n\nIn case the build process of your project consists of several independent\nsteps, you can still create one single *cmds.txt* file using\n-a (--append) option:\n\n.. code-block:: bash\n\n $ clade -i make step_one\n $ clade -i -a make step_two\n\nAs a result, build commands of the second make command will be appended\nto the *cmds.txt* file created previously.\n\nYou can intercept build commands from a python script:\n\n.. code-block:: python\n\n from clade import Clade\n c = Clade(cmds_file=\"cmds.txt\")\n c.intercept(command=[\"make\"], append=False)\n\n\nClade implements several different methods of build commands intercepting.\n\n\nLibrary injection\n~~~~~~~~~~~~~~~~~\n\nClade can intercept the *exec* calls issued by the build tool for each build\ncommand.\nTo do this we have developed a shared library (called *libinterceptor*)\nthat redefine such exec functions: before creating a new process our\nexec functions store the information about the command into a separate file.\nThe library is than injected into the build process using\n*LD_PRELOAD* (Linux) and *DYLD_INSERT_LIBRARIES* (macOS) mechanisms provided by\nthe dynamic linker.\n\n.. image:: docs/pics/libinterceptor.png\n :alt: An explanation of LD_PRELOAD\n\nLibrary injection is used by default.\n\nWrappers\n~~~~~~~~\n\nThere is an alternative intercepting method that is based on\n*wrappers*. It can be used when LD_PRELOAD is unavailable:\n\n.. code-block:: bash\n\n $ clade -i -wr make\n\nClade scans PATH environment variable to detect available\nexecutable files.\nThen it creates a temporary directory and creates\nwrappers for all this executables.\nEach wrapper simply logs arguments with which it was called and\nthen executes original executable.\nTo ensure that wrapper will be called instead of the original command\nClade adds this temporary directory to the PATH.\n\nThis method can't intercept commands that are executed\nbypassing the PATH environment variable: for example, *gcc* command can be\nintercepted, but calling directly to */usr/bin/gcc* cannot.\nIf you need to intercept such commands you may use \"Wrapper.wrap_list\"\nconfiguration option (read about configuration in the configuration_ section).\nFiles specified in \"Wrapper.wrap_list\" will be temporarily replaced\nby wrappers (in some cases it may require administrative privileges).\nIt is possible to specify directories in \"Wrapper.wrap_list\":\nin that case all executable files in them will be replaced by wrappers.\n\nYou can intercept build commands with wrappers from a python script:\n\n.. code-block:: python\n\n from clade import Clade\n\n conf = {\"Wrapper.wrap_list\": [\"/usr/bin/gcc\", \"~/.local/bin\"]\n c = Clade(cmds_file=\"cmds.txt\")\n c.intercept(command=[\"make\"], use_wrappers=True, conf=conf)\n\n\nWindows debugging API\n~~~~~~~~~~~~~~~~~~~~~\n\nWrappers and library injection works only on Linux and macOS.\nTo intercept build commands on Windows we have implemented another approach\nthat is based on the Windows debugging API.\nThe API provides the mechanism for the debugger to be notified of debug events\nfrom the process being debugged and to pause the target process until the\nevent has been processed.\n\nWe have developed a simple debugger that can be used to debug the build\nprocess.\nIt waits for the process start events, which corresponds to the execution of the build\ncommand, pauses the build process and reads memory of the newly created process\nto find and log its command line arguments, and then resumes the build process.\n\nIt can be used like this:\n\n.. code-block:: bash\n\n $ clade -i msbuild MyProject.sln\n\nYou can intercept build commands on Windows from a python script:\n\n.. code-block:: python\n\n from clade import Clade\n\n c = Clade(cmds_file=\"cmds.txt\")\n c.intercept(command=[\"msbuild\", \"MyProject.sln])\n\n\nContent of *cmds.txt* file\n--------------------------\n\nLet's look at the simple makefile:\n\n.. code-block:: make\n\n all:\n gcc main.c -o main\n rm main\n\nIf we try to intercept *make all* command,\nthe following *cmds.txt* file will be produced (on macOS):\n\n::\n\n /work/simple_make||0||/usr/bin/make||make||all\n /work/simple_make||1||/Library/Developer/CommandLineTools/usr/bin/make||/Library/Developer/CommandLineTools/usr/bin/make||all\n /work/simple_make||2||/usr/bin/gcc||gcc||main.c||-o||main||-O3\n /work/simple_make||3||/Library/Developer/CommandLineTools/usr/bin/gcc||/Library/Developer/CommandLineTools/usr/bin/gcc||main.c||-o||main||-O3\n /work/simple_make||4||/usr/bin/xcrun||/usr/bin/xcrun||clang||main.c||-o||main||-O3\n /work/simple_make||5||/Library/Developer/CommandLineTools/usr/bin/clang||/Library/Developer/CommandLineTools/usr/bin/clang||main.c||-o||main||-O3\n /work/simple_make||6||/Library/Developer/CommandLineTools/usr/bin/clang||/Library/Developer/CommandLineTools/usr/bin/clang||-cc1||-triple||x86_64-apple-macosx10.14.0||-Wdeprecated-objc-isa-usage||-Werror=deprecated-objc-isa-usage||-emit-obj||-disable-free||-disable-llvm-verifier||-discard-value-names||-main-file-name||main.c||-mrelocation-model||pic||-pic-level||2||-mthread-model||posix||-mdisable-fp-elim||-fno-strict-return||-masm-verbose||-munwind-tables||-target-cpu||penryn||-dwarf-column-info||-debugger-tuning=lldb||-target-linker-version||409.12||-resource-dir||/Library/Developer/CommandLineTools/usr/lib/clang/10.0.0||-O3||-fdebug-compilation-dir||/work/simple_make||-ferror-limit||19||-fmessage-length||150||-stack-protector||1||-fblocks||-fencode-extended-block-signature||-fobjc-runtime=macosx-10.14.0||-fmax-type-align=16||-fdiagnostics-show-option||-fcolor-diagnostics||-vectorize-loops||-vectorize-slp||-o||/var/folders/w7/d45mjl5d79v0hl9gqzzfkdgh0000gn/T/main-de88a6.o||-x||c||main.c\n /work/simple_make||7||/Library/Developer/CommandLineTools/usr/bin/ld||/Library/Developer/CommandLineTools/usr/bin/ld||-demangle||-lto_library||/Library/Developer/CommandLineTools/usr/lib/libLTO.dylib||-dynamic||-arch||x86_64||-macosx_version_min||10.14.0||-o||main||/var/folders/w7/d45mjl5d79v0hl9gqzzfkdgh0000gn/T/main-de88a6.o||-lSystem||/Library/Developer/CommandLineTools/usr/lib/clang/10.0.0/lib/darwin/libclang_rt.osx.a\n /work/simple_make||2||/bin/rm||rm||main\n\n\nYou can try to use *cmds.txt* file directly, but its format is not quite\nuser-friendly and is subject to change.\nIt is a good idea not to rely on the format of *cmds.txt* file\nand use the interface module instead:\n\n.. code-block:: python\n\n from clade.cmds import get_all_cmds\n cmds = get_all_cmds(\"cmds.txt\")\n\nwhere *cmds* is a list of dictionaries representing each intercepted command.\nFor example, dictionary that represents *gcc* command from the above makefile\nlooks like this:\n\n.. code-block:: json\n\n {\n \"command\": [\n \"gcc\",\n \"main.c\",\n \"-o\",\n \"main\",\n \"-O3\"\n ],\n \"cwd\": \"/work/simple_make\",\n \"id\": \"3\",\n \"pid\": \"2\",\n \"which\": \"/usr/bin/gcc\"\n }\n\nwhere:\n\n- *command* - is intercepted command itself;\n- *cwd* - is a path to the directory where the command was executed;\n- *id* - is a unique identifier assigned to the command;\n- *pid* - is an identifier of the parent command\n (command that executed the current one - in our example\n it is an identifier of the make command);\n- *which* - path to an executable file that was executed\n as a result of this command.\n\n.. It should be noted that all other functionality available in Clade use\n.. *cmds.txt* file as input.\n.. Due to this you do not need to rebuild your project every time you want\n.. to use it - you can just use previously generated *cmds.txt* file.\n\nParsing of intercepted commands\n-------------------------------\n\nBuild command intercepting is performed internally by the *clade* command, so\nin most cases you do not need to thing about it.\nOnce build commands are intercepted they can be parsed to search for input\nand output files, and options. Currently there are *extensions* in Clade\nfor parsing following commands:\n\n- C compilation commands (cc, gcc, clang, various cross compilers);\n- linker commands (ld);\n- assembler commands (as);\n- archive commands (ar);\n- move commands (mv);\n- object copy commands (objcopy, Linux only);\n- Microsoft CL compilation commands;\n- Microsoft linker commands;\n\nThese extensions can be executed from command line through *clade -e EXTENSION_NAME*,\nwhere EXTENSION_NAME can be CC, LD, AS, AR, MV, Objcopy, CL, or Link, like this:\n\n.. code-block:: bash\n\n $ clade -e CC make\n\nAs a result, a working directory named *clade* will be created:\n\n::\n\n clade/\n \u251c\u2500\u2500 cmds.txt\n \u251c\u2500\u2500 CC/\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 cmds.json\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 cmds/\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 deps/\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 opts/\n \u2502\u00a0\u00a0 \u2514\u2500\u2500 raw/\n \u251c\u2500\u2500 PidGraph/\n \u2514\u2500\u2500 Storage/\n\nTop-level directories are in turn working directories of corresponding\nextensions that were executed inside *clade* command.\n*CC* extension is the one we wanted to execute, but there are also\nother extensions - *PidGraph* and *Storage* - that were executed implicitly\nby *CC* because it depends on the results of their work.\nLet's skip them for now.\n\nInside *CC* directory there is a bunch of other directories and *cmds.json*\nfile with parsed compilation commands.\nAgain, it is a list of dictionaries representing each parsed command.\nLet's look at the parsed command from the above example:\n\n.. code-block:: json\n\n {\n \"cwd\":\"/work/simple_make\",\n \"id\":\"3\",\n \"in\":[\n \"main.c\"\n ],\n \"out\":[\n \"main\"\n ]\n }\n\nIts structure is quite simple: there is a list of input files,\na list of output files, unique identifier of the command, and\nthe directory where the command was executed.\n\nUsing the identifier of the command it is possible to get some additional information,\nlike its options.\nOptions of all parsed commands are located in the separated json files\ninside *opts* folder.\nOptions of the command with *id=\"3\"* are located in the *opts/3.json* file\nand look like this:\n\n.. code-block:: json\n\n [\n \"-O3\"\n ]\n\nRaw unparsed commands are located in the *raw* folder.\nIts structure resembles the structure of the *opts* folder, so the\nraw command of the command with id = 3 is located in the \"raw/3.json file\nand look like this:\n\n.. code-block:: json\n\n [\n \"gcc\",\n \"main.c\",\n \"-o\",\n \"main\",\n \"-O3\"\n ],\n\n*CC* extension also identify *dependencies* of the main source file\nfor each compilation command.\nDependencies are the names of all included header files,\neven ones included indirectly.\nClade stores them inside *deps* subfolder.\nFor example, dependencies of the parsed command with *id=\"3\"* can be found\nin *deps/3.json* file:\n\n.. code-block:: json\n\n [\n \"/usr/include/secure/_common.h\",\n \"/usr/include/sys/_types/_u_int32_t.h\",\n \"/usr/include/machine/_types.h\",\n \"/usr/include/sys/_types/_u_int16_t.h\",\n \"/usr/include/_stdio.h\",\n \"/usr/include/sys/cdefs.h\",\n \"/usr/include/secure/_stdio.h\",\n \"/usr/include/sys/_types/_size_t.h\",\n \"/usr/include/sys/_types/_u_int8_t.h\",\n \"/usr/include/stdio.h\",\n \"/usr/include/sys/_types/_ssize_t.h\",\n \"/usr/include/sys/_symbol_aliasing.h\",\n \"/usr/include/sys/_types/_int32_t.h\",\n \"/usr/include/sys/_pthread/_pthread_types.h\",\n \"/usr/include/sys/_types/_int8_t.h\",\n \"main.c\",\n \"/usr/include/sys/_types/_int16_t.h\",\n \"/usr/include/sys/_types/_uintptr_t.h\",\n \"/usr/include/sys/_types/_null.h\",\n \"/usr/include/sys/_types/_off_t.h\",\n \"/usr/include/sys/stdio.h\",\n \"/usr/include/_types.h\",\n \"/usr/include/AvailabilityInternal.h\",\n \"/usr/include/sys/_types/_va_list.h\",\n \"/usr/include/Availability.h\",\n \"/usr/include/sys/_posix_availability.h\",\n \"/usr/include/sys/_types/_u_int64_t.h\",\n \"/usr/include/sys/_types/_intptr_t.h\",\n \"/usr/include/sys/_types.h\",\n \"/usr/include/sys/_types/_int64_t.h\",\n \"/usr/include/i386/_types.h\",\n \"/usr/include/i386/types.h\",\n \"/usr/include/machine/types.h\"\n ]\n\nBesides dependencies, all other parsed commands (ld, mv, and so on)\nwill also look this way: as a list of dictionaries representing each\nparsed command, with \"id\", \"in\", \"out\" and \"cwd\" fields.\n\nAll data generated by *CC* extension (and by all other extensions, of course)\ncan also be used through Python interface:\n\n.. code-block:: python\n\n from clade import Clade\n\n # Initialize interface class with a path to the working directory\n # and a path to the file with intercepted commands\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\")\n\n # Get a list of all parsed commands\n for cmd in c.get_all_cmds_by_type(\"CC\"):\n # Get a list of dependencies\n deps = c.get_cmd_deps(cmd[\"id\"])\n # Get options\n opts = c.get_cmd_opts(cmd[\"id])\n # Get raw unparsed command\n raw = c.get_cmd_raw(cmd[\"id])\n ...\n\nPid graph\n---------\n\nEach intercepted command, except for the first one, is executed by another,\nparent command. For example, *gcc* internally executes\n*cc1* and *as* commands, so *gcc* is their parent.\nClade knows about this connection and tracks it by assigning to each intercepted\ncommand two attributes: a unique identifier (id) and identifier of its parent\n(pid).\nThis information is stored in the *pid graph* and can be obtained using\n*PidGraph* extension:\n\n.. code-block:: bash\n\n $ clade -e PidGraph make\n $ tree clade -L 2\n\n clade\n \u251c\u2500\u2500 cmds.txt\n \u2514\u2500\u2500 PidGraph\n \u00a0\u00a0 \u251c\u2500\u2500 pid_by_id.json\n \u00a0\u00a0 \u2514\u2500\u2500 pid_graph.json\n\nTwo files will be generated. First one - *pid_by_id.json* - is a simple\nmapping from ids to their pids and looks like this:\n\n.. code-block:: json\n\n {\n \"1\": \"0\",\n \"2\": \"1\",\n \"3\": \"2\",\n \"4\": \"2\",\n \"5\": \"1\"\n }\n\nAnother one - *pid_graph.json* - stores information about all parent commands\nfor a given id:\n\n.. code-block:: json\n\n {\n \"1\": [\"0\"],\n \"2\": [\"1\", \"0\"],\n \"3\": [\"2\", \"1\", \"0\"],\n \"4\": [\"2\", \"1\", \"0\"],\n \"5\": [\"1\", \"0\"]\n }\n\n*Pid graph* can be used through Python interface:\n\n.. code-block:: python\n\n from clade import Clade\n\n # Initialize interface class with a path to the working directory\n # and a path to the file with intercepted commands\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\")\n c.parse(\"PidGraph)\n\n # Get all information\n pid_graph = c.pid_graph\n pid_by_id = c.pid_by_id\n\nOther extensions use *pid graph* to filter *duplicate* commands.\nFor example, on macOS executing \"*gcc main.c*\" command leads to the\nchain of execution of the following commands:\n\n- /usr/bin/gcc main.c\n- /Library/Developer/CommandLineTools/usr/bin/gcc main.c\n- /usr/bin/xcrun clang main.c\n- /Library/Developer/CommandLineTools/usr/bin/clang main.c\n- /Library/Developer/CommandLineTools/usr/bin/clang -cc1 ...\n\nSo, for a single compilation command, several commands will be actually\nintercepted. You probably need only one of them (the very first one),\nso Clade filter all *duplicate* ones using *pid graph*: Clade simply\ndo not parse all child commands of already parsed command.\nThis behavior is of course configurable and can be disabled.\n\n*Pid graph* can be visualized with Graphviz using one of\nthe configuration options:\n\n.. image:: docs/pics/pid_graph.png\n :alt: An example of the pid graph\n\nNote: *pid graph* can be used with any project\n(not only with ones written in C).\n\nCommand graph\n-------------\n\nClade can connect commands by their input and output files.\nThis information is stored in the *command graph* and can be obtained using\n*CmdGraph* extension.\n\nTo appear in the *command graph* an intercepted command needs to be parsed\nto search for input and output files.\nBy default commands parsed by *CC*, *LD*, *MV*, \"AR\", \"AS\", \"Objcopy\"\nextensions are parsed and appeared in the *command graph*.\nThis behavior can be changed via configuration, which will be described below.\n\n\nLet's consider the following makefile:\n\n.. code-block:: make\n\n all:\n gcc -S main.c -o main.s # id = 1\n as main.s -o main.o # id = 2\n mv main.o main # id = 3\n\nUsing *CmdGraph* these commands can be connected:\n\n.. code-block:: bash\n\n $ clade -e CmdGraph make\n\n clade/\n \u251c\u2500\u2500 cmds.txt\n \u251c\u2500\u2500 CmdGraph/\n \u2502\u00a0\u00a0 \u2514\u2500\u2500 cmd_graph.json\n \u251c\u2500\u2500 CC/\n \u251c\u2500\u2500 LD/\n \u251c\u2500\u2500 MV/\n \u251c\u2500\u2500 PidGraph/\n \u2514\u2500\u2500 Storage/\n\nwhere *cmd_graph.json* looks like this (commands are represented by their\nidentifiers and the type of extensions that parsed it):\n\n.. code-block:: json\n\n {\n \"1\":{\n \"type\": \"CC\",\n \"used_by\": [\"2\", \"3\"],\n \"using\": []\n },\n \"2\":{\n \"type\": \"AS\",\n \"used_by\": [\"3\"],\n \"using\": [\"1\"]\n },\n \"3\":{\n \"type\": \"MV\",\n \"used_by\": [],\n \"using\": [\"1\", \"2\"]\n }\n }\n\n*Command graph* can be used through Python interface:\n\n.. code-block:: python\n\n from clade import Clade\n\n # Initialize interface class with a path to the working directory\n # and a path to the file with intercepted commands\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\")\n\n # Get the command graph\n cmd_graph = c.cmd_graph\n\n*Command graph* can be visualized with Graphviz using one of\nthe configuration options:\n\n.. image:: docs/pics/cmd_graph.png\n :alt: An example of the command graph\n\nSource graph\n------------\n\nFor a given source file Clade can show in which commands this file\nis compiled, and in which commands it is indirectly used.\nThis information is called *source graph* and can be generated\nusing *SrcGraph* extension:\n\n.. code-block:: bash\n\n $ clade -e SrcGraph make\n\n clade/\n \u251c\u2500\u2500 cmds.txt\n \u251c\u2500\u2500 SrcGraph/\n \u2502\u00a0\u00a0 \u2514\u2500\u2500 src_graph.json\n \u251c\u2500\u2500 CmdGraph/\n \u251c\u2500\u2500 CC/\n \u251c\u2500\u2500 LD/\n \u251c\u2500\u2500 MV/\n \u251c\u2500\u2500 PidGraph/\n \u2514\u2500\u2500 Storage/\n\n*Source graph* for the Makefile presented in the *command graph* section above\nwill be located in the *src_graph.json* file and look like this:\n\n.. code-block:: json\n\n {\n \"/usr/include/stdio.h\": {\n \"compiled_in\": [\"1\"],\n \"loc\": 414,\n \"used_by\": [\"2\", \"3\"]\n },\n \"main.c\":{\n \"compiled_in\": [\"1\"],\n \"loc\": 5,\n \"used_by\": [\"2\", \"3\"],\n },\n \"main.s\":{\n \"compiled_in\": [\"2\"],\n \"loc\": 20,\n \"used_by\": [\"3\"],\n }\n }\n\nFor simplicity information about other files has been removed from\nthe presented *source graph*.\nAs always, commands are represented through their unique identifiers.\n*loc* field contains information about the size of the source file:\nnumber of the lines of code.\n\n*Source graph* can be used through Python interface:\n\n.. code-block:: python\n\n from clade import Clade\n\n # Initialize interface class with a path to the working directory\n # and a path to the file with intercepted commands\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\")\n\n # Get the source graph\n src_graph = c.src_graph\n\nCall graph\n----------\n\nClade can generate function *call graph* for a given project written in C.\nThis requires CIF installed on your computer, and path to its bin directory\nadded to the PATH environment variable.\n\n*Call graph* can be generated using *Callgraph* extension:\n\n.. code-block:: bash\n\n $ clade -e Callgraph cmds.txt\n\n clade/\n \u251c\u2500\u2500 cmds.txt\n \u251c\u2500\u2500 Callgraph/\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 callgraph/\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 callgraph.json\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 calls_by_ptr.json\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 used_in.json\n \u2502\u00a0\u00a0 \u2514\u2500\u2500 err.log\n \u251c\u2500\u2500 CC/\n \u251c\u2500\u2500 LD/\n \u251c\u2500\u2500 MV/\n \u251c\u2500\u2500 PidGraph/\n \u251c\u2500\u2500 Info/\n \u251c\u2500\u2500 Functions/\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 functions_by_file/\n \u2502\u00a0\u00a0 \u251c\u2500\u2500 functions_by_file.json\n \u2502\u00a0\u00a0 \u2514\u2500\u2500 functions.json\n \u2514\u2500\u2500 Storage/\n\n*Call graph* itself is stored inside *callgraph.json* file and can be\nrather large. Let's look at a small part of the call graph generated for\nthe Linux kernel:\n\n.. code-block:: json\n\n {\n \"drivers/net/usb/asix_common.c\": {\n \"asix_get_phy_addr\": {\n \"called_in\": {\n \"drivers/net/usb/asix_devices.c\": {\n \"ax88172_bind\": {\n \"242\": {\"match_type\" : 1}\n },\n \"ax88178_bind\": {\n \"809\": {\"match_type\" : 1}\n }\n }\n },\n \"calls\": {\n \"drivers/net/usb/asix_common.c\": {\n \"asix_read_phy_addr\": {\n \"235\": {\"match_type\" : 5}\n }\n }\n },\n \"type\": \"extern\"\n }\n }\n }\n\nThere is \"drivers/net/usb/asix_common.c\" file with definition of the\n\"asix_get_phy_addr\" function. This function is called in the\n\"drivers/net/usb/asix_devices.c\" file by \"ax88172_bind\" function on line\n\"242\" and by \"ax88178_bind\" function on line \"809\". \"match_type\" is an internal\ninformation needed for debug purposes. Also this function calls \"asix_read_phy_addr\"\nfile from the \"drivers/net/usb/asix_common.c\" file on the line \"235\".\n\nAll functions that call \"asix_get_phy_addr\" function or are called by it are\nalso present in the *call graph*, but were excluded from the above example.\n\n*Callgraph* extension uses \"Function\" extension to get information about\nfunction definitions and declarations.\nThey are stored in the *functions.json* file:\n\n.. code-block:: json\n\n {\n \"asix_get_phy_addr\": {\n \"drivers/net/usb/asix_common.c\": {\n \"declarations\": {\n \"drivers/net/usb/asix.h\": {\n \"line\": \"204\",\n \"signature\": \"int asix_get_phy_addr(struct usbnet *);\",\n \"type\": \"extern\"\n }\n },\n \"line\": \"232\",\n \"signature\": \"int asix_get_phy_addr(struct usbnet *dev);\",\n \"type\": \"extern\"\n }\n }\n\nFor each function definition there is information about corresponding\ndeclaration, line numbers in which the definition and declaration are located,\nfunction signature and type (global or static).\n\n*Callgraph* and *Functions* can be used through Python interface:\n\n.. code-block:: python\n\n from clade import Clade\n\n # Initialize interface class with a path to the working directory\n # and a path to the file with intercepted commands\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\")\n\n # Get the call graph\n callgraph = c.callgraph\n\n # Usage looks quite ugly, yes\n # This will be improved\n for file in callgraph:\n for func in callgraph[file]:\n for caller_file in callgraph[file][func][\"called_in\"]:\n for caller_func in callgraph[file][func][\"called_in\"][caller_file]:\n for call_line in callgraph[file][func][\"called_in\"][caller_file][caller_func]:\n ...\n\n for called_file in callgraph[file][func][\"calls\"]:\n for called_func in callgraph[file][func][\"calls\"][called_file]:\n for call_line in callgraph[file][func][\"calls\"][called_file][called_func]:\n ...\n\n functions = c.functions\n # The usage is quite similar, so it is omitted\n ...\n\nCompilation database\n--------------------\n\nCommand line tool for generating compilation database has a different\ninterface, compared to most other command line tools available in Clade.\nCompilation database can be generated using *clade-cdb* command:\n\n.. code-block:: bash\n\n $ clade-cdb make\n\nwhere *make* should be replaced by your project build command.\nAs a result your project will be build and the *compile_commands.json*\nfile will be created in the current directory.\n\nIf you have *cmds.txt* file you can skip the build process and get\n*compile_comands.json* much faster:\n\n.. code-block:: bash\n\n $ clade-cdb --cmds cmds.txt\n\nOther options are available through --help option.\n\n*Compilation database* can be used through Python interface:\n\n.. code-block:: python\n\n from clade import Clade\n\n # Initialize interface class with a path to the working directory\n # and a path to the file with intercepted commands\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\")\n\n # Intercept build commands\n # This step can be skipped if build commands are already intercepted\n c.intercept(command=[\"make\"], append=False, use_wrappers=False)\n\n # Parse intercepted commands and generate compilation database\n c.parse(\"CDB\")\n\n # Get compilation database\n compilation_database = c.compilation_database\n\n=============\nConfiguration\n=============\n\nThere is a bunch of options that can be changed to alter the behaviour of the\n*clade* command. The configuration can be passed via the \"-c\" option like this:\n\n.. code-block:: bash\n\n $ clade -c conf.json make\n\nwhere *conf.json* is a json file with some configuration options:\n\n.. code-block:: json\n\n {\n \"PidGraph.as_picture\": true,\n \"CmdGraph.requires\": [\n \"CC\",\n \"LD\",\n \"MV\",\n \"AR\",\n \"Objcopy\"\n ],\n \"CC.which_list\": [\"/usr.bin.gcc\", \"^.*clang$\"]\n }\n\nThe configuration can be also passed as a Python dictionary:\n\n.. code-block:: python\n\n from clade import Clade\n\n conf = {\"PidGraph.as_picture\": True}\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\", conf=conf)\n\nwhich list\n----------\n\nLet's highlight some notable configuration options and let's start with\noptions for extensions that parse intercepted commands to search for input\nand output files, and options. These extensions need to know which commands\nto parse. They have a list of predefined regular expressions that they try\nto match with the *which* field of an intercepted command.\nFor example, *CC* extension have the following list:\n\n.. code-block:: json\n\n [\n \"cc$\",\n \"cc1$\",\n \"[mg]cc(-?\\\\d+(\\\\.\\\\d+){0,2})?$\",\n \"clang(-?\\\\d+(\\\\.\\\\d+){0,2})?$\"\n ]\n\nObviously, execution of */usr/bin/gcc* will be matched, as well as\n*/usr/bin/clade*, or */usr/local/bin/powerpc-elf-gcc-7*, so all such commands\nwill be treated as compilation commands and parsed accordingly.\nSometimes this list is not enough, so there is an option to change it:\n\n::\n\n \"CC.which_list\": [\"regexp_to_match_your_compiler\"]\n\nOptions for other such extensions look the same, you just need to replace *CC*\nby the name of the extension, so, for example, \"LD.which_list\" will be the\noption to change the list of regexes for *LD* extension.\n\nVisualization options\n---------------------\n\nCurrently there are two small options to visualize *pid graph* and *cmd graph*\nusing Graphviz:\n\n.. code-block:: json\n\n {\n \"PidGraph.as_picture\": true,\n \"CmdGraph.as_picture\": true\n }\n\nIf they are set, then next to *pid_graph.json* and *cmd_graph.json* files\nrespectively pdf files containing Graphviz output will appear.\n\nList of commands to parse\n-------------------------\n\nIf you want to generate *command graph*, or *source graph*, or *call graph*,\nthen you need to specify which commands to parse via \"CmdGraph.requires\"\noption. By default all commands that are supported now are parsed,\nbut you can reduce their number:\n\n.. code-block:: json\n\n {\n \"CmdGraph.requires\": [\"CC\", \"LD\"\"]\n }\n\nPresets\n-------\n\nThere is predefined set of options for the following projects that can be used\nin addition to user-defined configuration:\n\n- Linux kernel (preset linux_kernel)\n- Busybox (presets busybox_linux, busybox_macos)\n- Apache (presets apache_linux, apache_macos)\n\nIf you want to execute Clade on one of these projects then it might be a *good\nidea* to use this presets, since they will definitely save you from having\nto deal with various problems and mess with the configuration:\n\n.. code-block:: bash\n\n $ clade -p linux_kernel make\n\nor\n\n.. code-block:: python\n\n from clade import Clade\n\n c = Clade(work_dir=\"clade\", cmds_file=\"cmds.txt\", preset=\"linux_kernel\")\n\n===============\nTroubleshooting\n===============\n\nFile with intercepted commands is empty\n---------------------------------------\n\nAccess control mechanisms on different operating systems might disable\nlibrary injection that is used by Clade to intercept build commands:\n\n- SELinux on Fedora, CentOS, RHEL;\n- System Integrity Protection on macOS;\n- Mandatory Integrity Control on Windows (disables similar mechanisms)\n\nA solution is to use another intercepting mechanism that is based on\n*wrappers*.\n\nFile with intercepted commands is not complete\n----------------------------------------------\n\nSometimes some commands are intercepted, so file *cmds.txt* is present and not\nempty, but other commands are clearly missing.\nSuch behaviour should be reported so the issue can be fixed, but until then\nyou can try to use another intercepting mechanism that is based on\n*wrappers*.\n\nWrong ELF class\n---------------\n\nBuild command intercepting may result in the following error:\n\n::\n\n ERROR: ld.so: object 'libinterceptor.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS64): ignored.\n\nIt is because your project leverages multilib capabilities, but\n*libinterceptor* library that is used to intercept build commands is\ncompiled without multilib support.\nYou need to install *gcc-multilib* (Ubuntu) or *gcc-32bit* (openSUSE) package\nand **reinstall Clade**. *libinterceptor* library will be recompiled and your\nissue will be fixed.\n\nNot all intercepted compilation commands are parsed\n---------------------------------------------------\n\nThe reason is because *CC* extension that parse intercepted commands cannot\nidentify a command as a compilation command. You can help it by specifying\n\"CC.which_list\" configuration option, in which you should write a list of\nregexes that will match your compiler. For example, if path to your compiler\nis *~/.local/bin/c_compiler*, than \"CC.which_list\" may be set like this:\n\n::\n\n \"CC.which_list\": [\"^.*?c_compiler$\"]\n\nIf you want to parse not only commands executed by your compiler, but by system\n*gcc* as well, then you can add it to the list too:\n\n::\n\n \"CC.which_list\": [\"^.*?c_compiler$\", \"\"^.*gcc$\"]\n\nHow to set configuration option is described in *Configuration* section of\nthis readme.\n\nCompilation database miss some commands\n---------------------------------------\n\nSame as above.\n\nCommand graph is not connected properly\n---------------------------------------\n\nMost certainly it is due to the fact that some type of commands is unparsed.\nIf there is an extension in Clade that can parse them, then you will need\nto specify it via the option \"CmdGraph.requires\":\n\n.. code-block:: json\n\n {\n \"CmdGraph.requires\": [\"CC\", \"LD\", \"MV\", \"AR\", \"Objcopy\"]\n }\n\nOtherwise such extension should be developed.\n\nSimilar problems with the *source graph* and the *call graph* can be fixed\nvia the same option, since they use the *command graph* internally.\n\n===============\nAcknowledgments\n===============\n\nClade is inspired by the Bear_ project created by `L\u00e1szl\u00f3 Nagy`_.\n\n.. _Bear: https://github.com/rizsotto/Bear\n.. _L\u00e1szl\u00f3 Nagy: https://github.com/rizsotto", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/17451k/clade", "keywords": "", "license": "LICENSE.txt", "maintainer": "", "maintainer_email": "", "name": "clade", "package_url": "https://pypi.org/project/clade/", "platform": "", "project_url": "https://pypi.org/project/clade/", "project_urls": { "Homepage": "https://github.com/17451k/clade" }, "release_url": "https://pypi.org/project/clade/3.2.4/", "requires_dist": null, "requires_python": ">=3.4", "summary": "Clade is a tool for extracting information about software build process and source code", "version": "3.2.4" }, "last_serial": 5972309, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "4cf0319f3b0c3805993821e33a017927", "sha256": "3261984be93f5dffc7d3dd98ad7b0f756cb072d4aa088e80e11e743db41a80f3" }, "downloads": -1, "filename": "clade-1.02-cp37-cp37m-macosx_10_13_x86_64.whl", "has_sig": false, "md5_digest": "4cf0319f3b0c3805993821e33a017927", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": null, "size": 19803, "upload_time": "2018-07-23T11:13:19", "url": "https://files.pythonhosted.org/packages/2a/bb/f34133021f52e44e0fd8f79dc6653b464132178c65f0d57885401d77c5fb/clade-1.02-cp37-cp37m-macosx_10_13_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "aaf76f13f2fd11ffe0b7f2434fe259ba", "sha256": "6cedec6421022c476e8c311681fb7aec2deea7be0731a44c1343b0a98aca7c80" }, "downloads": -1, "filename": "clade-1.02.tar.gz", "has_sig": false, "md5_digest": "aaf76f13f2fd11ffe0b7f2434fe259ba", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12791, "upload_time": "2018-07-23T11:13:20", "url": "https://files.pythonhosted.org/packages/65/ca/8d7224a9f97a8fdc5c1d497a06a4177c3edf4dae089856a456d4a8d10803/clade-1.02.tar.gz" } ], "2.0": [ { "comment_text": "", "digests": { "md5": "515445805c1df7f6161f827107d525f1", "sha256": "7608f6149065354cef9079f1af23d99a5370183da3cafff8f4f9cb1cc013ab87" }, "downloads": -1, "filename": "clade-2.0-cp37-cp37m-macosx_10_13_x86_64.whl", "has_sig": false, "md5_digest": "515445805c1df7f6161f827107d525f1", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 89080, "upload_time": "2018-10-19T17:39:32", "url": "https://files.pythonhosted.org/packages/88/ce/169c61ff903798c5c5b0ef1f851c1d53302d91965093e98b8e8eca86ca2c/clade-2.0-cp37-cp37m-macosx_10_13_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "eaa6c15ca08f0a1cd47f112479d1ab31", "sha256": "b005234da3be99fe40b077b6d8d1228b441228ef82f984a253c8528d14a640d0" }, "downloads": -1, "filename": "clade-2.0.tar.gz", "has_sig": false, "md5_digest": "eaa6c15ca08f0a1cd47f112479d1ab31", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 72536, "upload_time": "2018-10-19T17:39:33", "url": "https://files.pythonhosted.org/packages/c7/8e/9c2810525d32cc46d20de65ff2a6b15b30003685d14025e4338b58282daa/clade-2.0.tar.gz" } ], "2.0.1": [ { "comment_text": "", "digests": { "md5": "0e639e78e158224fd081c8aba5f93329", "sha256": "7554d3d9ef9ffb899bbf4b995bfe43905e8b5b7b6660ebc22dce5b75958b7b0a" }, "downloads": -1, "filename": "clade-2.0.1.tar.gz", "has_sig": false, "md5_digest": "0e639e78e158224fd081c8aba5f93329", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 165485, "upload_time": "2018-11-14T13:01:38", "url": "https://files.pythonhosted.org/packages/88/99/6ce93f32fbcbe1007ecd40b0d278d9b26e1a5ec29d9cc94ed8e10741227e/clade-2.0.1.tar.gz" } ], "2.1": [ { "comment_text": "", "digests": { "md5": "17c6aa0ced79ffdbc25aa51490881a85", "sha256": "d74b99a84662109bf6c5e28cce0357416fa2554450a82411216c62ae11ade2a3" }, "downloads": -1, "filename": "clade-2.1-src.tar.gz", "has_sig": false, "md5_digest": "17c6aa0ced79ffdbc25aa51490881a85", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 962128, "upload_time": "2018-12-17T15:12:39", "url": "https://files.pythonhosted.org/packages/90/ca/79904e84b51621124e92a3d4c6a980c86b5dcf53fbbd69d8bb64fb042f65/clade-2.1-src.tar.gz" } ], "2.2.dev1": [ { "comment_text": "", "digests": { "md5": "36c8161b9dc08d06d3c4b859ddf6eb94", "sha256": "a4df2070063fffecd9b5162f3874eb53a412a9e552d5dc86a5ac9d8acffd71a5" }, "downloads": -1, "filename": "clade-2.2.dev1.tar.gz", "has_sig": false, "md5_digest": "36c8161b9dc08d06d3c4b859ddf6eb94", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 308063, "upload_time": "2019-02-22T13:47:10", "url": "https://files.pythonhosted.org/packages/0d/06/8a521367c00c99bac7486e5074d23c134d8d688802c75b9e73bc6f5ede62/clade-2.2.dev1.tar.gz" } ], "2.2.dev2": [ { "comment_text": "", "digests": { "md5": "cd97629355e4354474f68cd0ca3d2580", "sha256": "15e7508c93a4d73d0e24a8905380dd11126dadd7bbe081b662512cd3375ce0c2" }, "downloads": -1, "filename": "clade-2.2.dev2.tar.gz", "has_sig": false, "md5_digest": "cd97629355e4354474f68cd0ca3d2580", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 979776, "upload_time": "2019-02-25T12:48:13", "url": "https://files.pythonhosted.org/packages/a7/90/f8c7e01177e1ccbae768824715fb7baf01445170004d8aa3b43a74f1fd6d/clade-2.2.dev2.tar.gz" } ], "3.0": [ { "comment_text": "", "digests": { "md5": "465ba25fb1168b8294fb3e462c71b55e", "sha256": "7c2d121fd44aabf3a8d93d07b14e1122b2305d790286014371a152fcfc1b1f79" }, "downloads": -1, "filename": "clade-3.0.tar.gz", "has_sig": false, "md5_digest": "465ba25fb1168b8294fb3e462c71b55e", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 987431, "upload_time": "2019-03-22T17:00:26", "url": "https://files.pythonhosted.org/packages/ab/d7/4823899ce4be5d3d898779076c79502a6565062ae7cab96eb86e5eda1596/clade-3.0.tar.gz" } ], "3.0.1": [ { "comment_text": "", "digests": { "md5": "c9b4e053c5548650c2d071a54d141af7", "sha256": "abdc7493bbef6c42ca652fe32248ce0dd4142f2441901911d56963e3a1b6e408" }, "downloads": -1, "filename": "clade-3.0.1.tar.gz", "has_sig": false, "md5_digest": "c9b4e053c5548650c2d071a54d141af7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 89201, "upload_time": "2019-03-26T09:24:36", "url": "https://files.pythonhosted.org/packages/64/09/377d9e30852e61bc0cf4927a67d4f0e6969fd3fe3e653027cc34bc06eecb/clade-3.0.1.tar.gz" } ], "3.0.2": [ { "comment_text": "", "digests": { "md5": "bf45b8478478a51b6eead5f60d377373", "sha256": "a6cc89c1994dd268f4b8b5ecc488679b4ebdf82acc5008754b81b2d8999f92f2" }, "downloads": -1, "filename": "clade-3.0.2.tar.gz", "has_sig": false, "md5_digest": "bf45b8478478a51b6eead5f60d377373", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 89210, "upload_time": "2019-04-11T09:45:00", "url": "https://files.pythonhosted.org/packages/c8/ce/67129e5eb86cb5626901e5015d0b6004a93d209df41bea252092f45bb14e/clade-3.0.2.tar.gz" } ], "3.0.3": [ { "comment_text": "", "digests": { "md5": "1ae19a7ba9dab8dea00e3087c4745b36", "sha256": "dd1f680b7a2177f2eb81dbb31a78ee50c4a9f83d5576bac76104a838bddc3cbd" }, "downloads": -1, "filename": "clade-3.0.3.tar.gz", "has_sig": false, "md5_digest": "1ae19a7ba9dab8dea00e3087c4745b36", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 89374, "upload_time": "2019-04-22T13:15:37", "url": "https://files.pythonhosted.org/packages/29/bf/005de43689d9e38337b31cb28f24bddbdf5cbd26f11b909c6d94b73644e0/clade-3.0.3.tar.gz" } ], "3.0.4": [ { "comment_text": "", "digests": { "md5": "30ceb3182675956d54a7eaf5391ee1f6", "sha256": "2bf846292ce6103168969f1b71b470f4751c133b4c8b1319996464e0fafbe6c4" }, "downloads": -1, "filename": "clade-3.0.4.tar.gz", "has_sig": false, "md5_digest": "30ceb3182675956d54a7eaf5391ee1f6", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 89310, "upload_time": "2019-06-03T12:40:29", "url": "https://files.pythonhosted.org/packages/2d/b2/0ab7fb1307f9035b348c995a6bbfe8a4f45f43b701dc376e479aee6cbfef/clade-3.0.4.tar.gz" } ], "3.0.5": [ { "comment_text": "", "digests": { "md5": "ec6569b5ae0def7d7b5dba747603abc2", "sha256": "ef858d175466516c485efddb883697cb8a341c6424e64774c6f2a48eaccbd3ce" }, "downloads": -1, "filename": "clade-3.0.5.tar.gz", "has_sig": false, "md5_digest": "ec6569b5ae0def7d7b5dba747603abc2", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 89311, "upload_time": "2019-06-04T14:51:57", "url": "https://files.pythonhosted.org/packages/2e/7b/ae4f2af13a0bab4f6d0f4727ebcb6099205bff40df9dd8136aef3dfa484b/clade-3.0.5.tar.gz" } ], "3.0.6": [ { "comment_text": "", "digests": { "md5": "c287b213c96a241144117e368cb9d76a", "sha256": "a1599b362105618a52e1fd1bbf6153903c90310decb6292e9b4c2441b8dd56ec" }, "downloads": -1, "filename": "clade-3.0.6.tar.gz", "has_sig": false, "md5_digest": "c287b213c96a241144117e368cb9d76a", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 89430, "upload_time": "2019-06-11T09:52:54", "url": "https://files.pythonhosted.org/packages/af/59/3ab4ee8136cc98a4fb85b1a314990a818626eaa715783fe09b198350c755/clade-3.0.6.tar.gz" } ], "3.1": [ { "comment_text": "", "digests": { "md5": "feebe865192a555bdadc6b084fef4674", "sha256": "da9be5328749cc61842f8ae5f5cd2fe1befd24c312f4913c7efab2e611f5a66f" }, "downloads": -1, "filename": "clade-3.1-cp37-cp37m-macosx_10_14_x86_64.whl", "has_sig": false, "md5_digest": "feebe865192a555bdadc6b084fef4674", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 122560, "upload_time": "2019-08-09T11:42:03", "url": "https://files.pythonhosted.org/packages/ff/3e/6ebce80ceb90c30e5ecdce7b6f284c196a9f00e1fc5bea036311fac9540e/clade-3.1-cp37-cp37m-macosx_10_14_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "88ba9fb003855fbc9663e049f15663dc", "sha256": "db9f80599ed2f025a72cd75d768f7d9b094a5aa8ff756da2d83bcddde33a01b6" }, "downloads": -1, "filename": "clade-3.1-cp37-cp37m-win_amd64.whl", "has_sig": false, "md5_digest": "88ba9fb003855fbc9663e049f15663dc", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 150749, "upload_time": "2019-08-09T11:44:17", "url": "https://files.pythonhosted.org/packages/23/96/278570846c4a99578825f4f727fbc7fa297914cf4754e114cc0073168e2e/clade-3.1-cp37-cp37m-win_amd64.whl" }, { "comment_text": "", "digests": { "md5": "d1f82993f50b52e319f1c053b75ff14a", "sha256": "3f397f990d642f3acf88c24c61cc43a53399a61ff483d30ccb91ae39deafa116" }, "downloads": -1, "filename": "clade-3.1.tar.gz", "has_sig": false, "md5_digest": "d1f82993f50b52e319f1c053b75ff14a", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 94715, "upload_time": "2019-08-09T11:42:05", "url": "https://files.pythonhosted.org/packages/b9/15/999be6301d0afc723477c28502d7284b9e5cd980aa679b1af35bbf4a4605/clade-3.1.tar.gz" } ], "3.1.1": [ { "comment_text": "", "digests": { "md5": "c637f38d8367beb46f2353131513c138", "sha256": "81d376d36e4eb8454b05bd020fb7d74de745643f2fa57c7063888f0049bed096" }, "downloads": -1, "filename": "clade-3.1.1-cp37-cp37m-macosx_10_14_x86_64.whl", "has_sig": false, "md5_digest": "c637f38d8367beb46f2353131513c138", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 122889, "upload_time": "2019-08-14T08:30:55", "url": "https://files.pythonhosted.org/packages/44/20/621928be021284cd0a509bfa0ff13a52a75c66a2d78c1610de3e07ae2fbc/clade-3.1.1-cp37-cp37m-macosx_10_14_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "7b6a4202d15368aad18863d48df2406e", "sha256": "dcfac51bd71622fc7c529a33f5e46caedce815b77a18215e2ce91972a5b039d6" }, "downloads": -1, "filename": "clade-3.1.1-cp37-cp37m-win_amd64.whl", "has_sig": false, "md5_digest": "7b6a4202d15368aad18863d48df2406e", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 151083, "upload_time": "2019-08-14T08:37:46", "url": "https://files.pythonhosted.org/packages/e6/de/25beea4c5d2d9c880fd8ba751dbb83dee895cc537ca21d69e9ae43e8a929/clade-3.1.1-cp37-cp37m-win_amd64.whl" }, { "comment_text": "", "digests": { "md5": "8960dbafcdce1dfb2457b7717e6f4dac", "sha256": "c3f5e95ba2e96721536b88b0a34551045df2903bc1e98d2acf1f3a6407fe4d8e" }, "downloads": -1, "filename": "clade-3.1.1.tar.gz", "has_sig": false, "md5_digest": "8960dbafcdce1dfb2457b7717e6f4dac", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 94760, "upload_time": "2019-08-14T08:30:59", "url": "https://files.pythonhosted.org/packages/b3/41/1e2366d8eefaba83b5eef73f05cf603c2e739e47d7d7cbf171f43c013495/clade-3.1.1.tar.gz" } ], "3.2": [ { "comment_text": "", "digests": { "md5": "2f5a5e8cf4dc16477c676fda26f49971", "sha256": "c456d1f774dfd64ba908654ba678414cc3eec0cce509beb9c22d207fa97fba22" }, "downloads": -1, "filename": "clade-3.2-cp37-cp37m-macosx_10_14_x86_64.whl", "has_sig": false, "md5_digest": "2f5a5e8cf4dc16477c676fda26f49971", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 135407, "upload_time": "2019-09-02T15:00:46", "url": "https://files.pythonhosted.org/packages/03/12/deae4f1ff1d848f96421388bdb8fd14afa0d9a994487953addc66b0dc9c8/clade-3.2-cp37-cp37m-macosx_10_14_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "deb65b0571d6f40a7d8956d98bba12de", "sha256": "a686b1d24c7a56d429b852b7b0bb356f909e5d7de2c339feff3664bcdfea61e0" }, "downloads": -1, "filename": "clade-3.2-cp37-cp37m-win_amd64.whl", "has_sig": false, "md5_digest": "deb65b0571d6f40a7d8956d98bba12de", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 153282, "upload_time": "2019-09-02T15:06:23", "url": "https://files.pythonhosted.org/packages/ee/80/493a9b1ceedd535dfb1a5a1b8409a72ac577f4d71e6b87c0e30135543fd0/clade-3.2-cp37-cp37m-win_amd64.whl" }, { "comment_text": "", "digests": { "md5": "3cf6097932f7eb5a5d14e90f4d33d81d", "sha256": "dc44ad3dd7b3dd83be5af76db55da6950e7b0bcd65805c694d96f48ccba0e72e" }, "downloads": -1, "filename": "clade-3.2.tar.gz", "has_sig": false, "md5_digest": "3cf6097932f7eb5a5d14e90f4d33d81d", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 96761, "upload_time": "2019-09-02T14:57:26", "url": "https://files.pythonhosted.org/packages/d1/de/b58330592d0f6a0bb257a82792a58af0a15926700573e6ace69e657d6f20/clade-3.2.tar.gz" } ], "3.2.1": [ { "comment_text": "", "digests": { "md5": "c312a40b6b7b2bc8bfebfb2df7575e88", "sha256": "54dd8c6b56df2eeb611d2d05144992c0dc3eb5ccb2b8c60606b58954b44caa70" }, "downloads": -1, "filename": "clade-3.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", "has_sig": false, "md5_digest": "c312a40b6b7b2bc8bfebfb2df7575e88", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 135836, "upload_time": "2019-09-16T13:22:33", "url": "https://files.pythonhosted.org/packages/8e/44/d890eedbde64e7da5ec27f08be2139e2922abbe5e2227744509471839b09/clade-3.2.1-cp37-cp37m-macosx_10_14_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "bea901c3899180b7a2a876c71acb5aa0", "sha256": "6f8e38df51bd277d194564d0a5abc5e9850d0493ec4404219ab793eaa78bf262" }, "downloads": -1, "filename": "clade-3.2.1-cp37-cp37m-win_amd64.whl", "has_sig": false, "md5_digest": "bea901c3899180b7a2a876c71acb5aa0", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 153698, "upload_time": "2019-09-16T13:19:33", "url": "https://files.pythonhosted.org/packages/53/07/304536b19badade701b36f342310a0b07f022bf004ed95219ffeae781db0/clade-3.2.1-cp37-cp37m-win_amd64.whl" }, { "comment_text": "", "digests": { "md5": "1f346e5ee24c5a76094ca3152b39d78c", "sha256": "97ba48add55967429906ef9355c4020cb36b8951f31a3a1e32c7ce1034dd3a41" }, "downloads": -1, "filename": "clade-3.2.1.tar.gz", "has_sig": false, "md5_digest": "1f346e5ee24c5a76094ca3152b39d78c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 96916, "upload_time": "2019-09-16T13:11:53", "url": "https://files.pythonhosted.org/packages/c4/8c/ac9de4e84aad367b87c13725aaeaf865aed97227ad5fd354c11ece05ea3f/clade-3.2.1.tar.gz" } ], "3.2.2": [ { "comment_text": "", "digests": { "md5": "72ddad6a6e9b582da79d72e7cb852934", "sha256": "da408aa928c9b97768566a0f884b32d78b44492e938a57e55475fa9de6870856" }, "downloads": -1, "filename": "clade-3.2.2-cp37-cp37m-macosx_10_14_x86_64.whl", "has_sig": false, "md5_digest": "72ddad6a6e9b582da79d72e7cb852934", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 125728, "upload_time": "2019-09-25T15:49:44", "url": "https://files.pythonhosted.org/packages/60/a9/dc04a25235d4c5a0e551b3902d04fbe1b26f6866d7634f228e62eac3eb95/clade-3.2.2-cp37-cp37m-macosx_10_14_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "6ae93c725de4d76c981ad6e643dbf4f8", "sha256": "bb0666e47f64971fd3904cf954c600700deeb605ffd21eedce2d817abf953c5a" }, "downloads": -1, "filename": "clade-3.2.2-cp37-cp37m-win_amd64.whl", "has_sig": false, "md5_digest": "6ae93c725de4d76c981ad6e643dbf4f8", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": ">=3.4", "size": 153939, "upload_time": "2019-09-25T15:50:55", "url": "https://files.pythonhosted.org/packages/a6/9c/a0959f740c7caffc76e2b490a8a5ec741df8d5e65c8590315419a294e0ea/clade-3.2.2-cp37-cp37m-win_amd64.whl" }, { "comment_text": "", "digests": { "md5": "3316292885944ad5c75cb9121eae5e59", "sha256": "337f3c8c01924caf53edf129c14196516992d4c9efa3f04ce0cbc6090e72dff1" }, "downloads": -1, "filename": "clade-3.2.2.tar.gz", "has_sig": false, "md5_digest": "3316292885944ad5c75cb9121eae5e59", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 97137, "upload_time": "2019-09-25T15:49:47", "url": "https://files.pythonhosted.org/packages/10/f9/fa164b3c55b7a150f76c2c57054b8cafea2956361c64d031b0bd5b176ee3/clade-3.2.2.tar.gz" } ], "3.2.3": [ { "comment_text": "", "digests": { "md5": "4aef7de0e11b7d7ce74f762c611cc5b2", "sha256": "4865b46d927f38c8df213f6c4b11b76a4290c66c9d921d5982605aeed0accfae" }, "downloads": -1, "filename": "clade-3.2.3.tar.gz", "has_sig": false, "md5_digest": "4aef7de0e11b7d7ce74f762c611cc5b2", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 97178, "upload_time": "2019-10-14T12:16:21", "url": "https://files.pythonhosted.org/packages/b4/25/1bc234189982dbc350c9d08f112da39af748e1eb1b2860b4beba0e248c49/clade-3.2.3.tar.gz" } ], "3.2.4": [ { "comment_text": "", "digests": { "md5": "b66483ba03aad1615e963fb5980a132c", "sha256": "3c69d705a6576be3f72a45c3e3058c4ee0c703f81e4f4c2b1a7ab907e6620c87" }, "downloads": -1, "filename": "clade-3.2.4.tar.gz", "has_sig": false, "md5_digest": "b66483ba03aad1615e963fb5980a132c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 97173, "upload_time": "2019-10-14T16:40:25", "url": "https://files.pythonhosted.org/packages/41/1c/970c9be23623c315bfab0678a1888b7c40b9f6158c6e28aada53a6c00e90/clade-3.2.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b66483ba03aad1615e963fb5980a132c", "sha256": "3c69d705a6576be3f72a45c3e3058c4ee0c703f81e4f4c2b1a7ab907e6620c87" }, "downloads": -1, "filename": "clade-3.2.4.tar.gz", "has_sig": false, "md5_digest": "b66483ba03aad1615e963fb5980a132c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4", "size": 97173, "upload_time": "2019-10-14T16:40:25", "url": "https://files.pythonhosted.org/packages/41/1c/970c9be23623c315bfab0678a1888b7c40b9f6158c6e28aada53a6c00e90/clade-3.2.4.tar.gz" } ] }