These commits are when the Protocol Buffers files have changed: (only the last 100 relevant commits are shown)
| Commit: | ba51998 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Disable preprocessing for the simple cache by default
| Commit: | cd4842b | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Run the local compilations with the nice of the client
| Commit: | bce9a26 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Count only the paid entries in the cache size The tags report is served from a snapshot: while the cleaner is busy, the request gets the old numbers at once with their age, and the refresh goes in background. The single pass over the entries and the blobs computes the freeable size without a forget-tag round per tag. Evict() and the statistics leave out the entries, whose outputs are linked with the build directories, and the entries of the tags with own sizes: they are never removed, so they can't be kept under the limit. The free ones are shown aside - "Cache size (X of Y + Z of free entries)".
| Commit: | 35e817a | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Let one source root cover all directories in it: "/dir/*" The checkouts in one directory don't need to be listed one by one: each directory in "/dir" is a root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 0fa57ab | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Tell, how the precompiled headers are shared between the roots Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | e9787da | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Key the precompiled header by its build directory The precompiled header has the paths of all headers: absolute ones, or relative to its root - "-isysroot" with "-relocatable-pch". Only if the root is a source root, and the build directory is inside it, the root is replaced in the key, and the place of the build directory in the root is a part of it. Otherwise the key has the current directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 3d3c21d | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Show the config, that the daemon has read, and why the cache is off - in --stats The daemon ignored a broken config silently, and the cache could be off without a word. Now the first lines of --stats show the version of the daemon, its config file - or the defaults - with the place of the error, if the file isn't used, and the directory of the cache, or why it's off: not configured, disabled, used by another daemon, of an incompatible format. The errors of the text protos tell the line and the column, instead of "Can't parse the file", and aren't printed by libprotobuf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 9ba844d | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Add dist-clang --stop - to apply the configuration of the daemon The help said to stop the daemon to apply the changes of its configuration, but there was no command for that. The daemon replies to the request, and then stops like with SIGTERM; the client waits until the daemon releases the lock of its socket. The daemon of root, that serves other users, is stopped only by root. The test connection now calls the callbacks of the sends, like the real one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 8d94578 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Don't call the hard links of the outputs read-only The links aren't read-only since long - their count is limited by max_output_links instead. The clones work on APFS too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | c9dd117 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Put the cache first in --help-config, and the distributed compilation last The obsolete fields and the ones, that aren't implemented, are deprecated in the proto and hidden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 645fe75 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Describe the isolation of the direct cache and the entries tagged before Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 40cf05a | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Warn about the unsupported options of the client config The thin client parsed the plugins, the version and rewrite_includes of .distclang - and DC_CLANG_VERSION - but never used them: e.g. the objects were silently built without the plugins. The old configs still parse, but the client warns about these options on stderr - its log isn't set up yet, when the config is read. DC_CLANG_VERSION is gone. The test of the plugins is replaced by the tests of what is used: the config in a parent directory, the relative path, the environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 47dc009 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Don't require the compiler in the config of the client A .distclang without the path - e.g. with only "disabled: true" - failed to parse, and was silently skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 519817e | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Configure the own sizes of the tags, and show the own rules of the tags Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 5378416 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Describe the tags in the configuration of the cache The tags get more rules than the isolation - e.g. their own sizes. Instead of the list of the isolated tags the configuration describes the tags with their own rules: "tags { name: ... isolated: true }". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 5e7253f | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Look up the cache in the scope of the isolated tag of the client The cache configuration lists the isolated tags - see "isolated_tags" - and the client passes its tags as before, with "--tag". The build with such tag finds only its own entries in the simple and the direct caches, and stores the direct entries in its scope. It also tracks its own edited and seen sources: the decision to preprocess doesn't depend on other builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 0072dc8 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Find only the direct entries of the isolated tag in its scope The direct entry, stored by others, could give the build of the isolated tag a direct hit, where its own history gives the preprocessing and a simple hit. Now the store in the scope of the tag adds its own row of the direct cache and marks the variant of the headers with the tag - the variant keeps it, when others store it again. The lookup in the scope tries only such variants and rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | e48109e | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Remove the direct entries, that lead to no entries The removal of the entries - by the eviction, or with their tags - left the manifests of the direct cache, that lead to them: they found nothing anymore, weren't shown by the statistics of the tags, and weren't removed by anything but the eviction from the full cache. E.g. 22,444 of them took 2.5 GiB after the removal of the only tag of the cache. Now the manifest lists the entries, that its variants lead to - and the index too: the direct entry is removed in the maintenance, when all of them are gone. The direct entries of the older revisions don't know their entries: they're removed after the start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | bb97b5c | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Restore the traces of the time with the hits, if the cache stores them With "store_time_traces" the compilations with -ftime-trace look up the cache again: only the entries with the trace of the same options - the granularity, the verbosity - are hits, and the trace is restored next to the object, with the paths of the current source root. The other entries are compiled again, and get the traces. Such compilations still skip the preprocessing: it changes the times in the traces of the others. Without the option the stored traces are removed after the start. E.g. to look for the expensive headers after every incremental build: the trace next to the object is of the compilation, that produced it - also after a branch switch, instead of a stale one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | df1cbe0 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Keep the traces of the time in the cache, if they're stored Another blob of the entry: the hit reads it only, if it's needed, and the lost trace doesn't lose the entry - e.g. after an older daemon, that doesn't count the references to it. The index marks the traces among the blobs: their size is shown apart, and the traces are removed after the start without the option - only the entries, that have them, are read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 1147449 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Compile without the lookups in the cache on the request of the client With "--recache" or "DC_RECACHE=1" in the environment - e.g. for the whole build, to measure it as without the cache: the compilations go as with -ftime-trace, and their results are still stored for the next builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | b7c5f0b | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Compile the sources with -ftime-trace without the lookups in the cache The hit gave the object without the trace - or with a stale one of an older build. Now the compilation with the trace goes as without the cache: neither the lookups, nor the preprocessing at the same time, that takes the CPU from the other compilations. The result is still stored - for the direct cache, without the flag in the key - so that the builds without the trace find it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 5f0fb91 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Uncompress the blob again, when it's used With the linked outputs the cold blobs are compressed - and stayed compressed forever: every hit of the entry unpacked and copied the outputs, and the new compilations of the same outputs weren't linked into the cache, since the compressed blob was reused. Now the hit uncompresses the blob to be linked, and the cleaner removes its compressed version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 0d3e086 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Keep the flags, that may fail the backend, in the key with recheck_warnings The recheck of the warnings runs without the code generation, and doesn't see the errors of the backend: e.g. a compilation with "-Werror=frame-larger-than" got the object, that it can't produce. Now the flags, that turn the warnings into errors, and the ones of the backend warnings stay in the key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | e5b75ce | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Replace the source root in the key only, where the compiler remaps it The compilation was relocatable with any prefix map, and the key replaced both the absolute root and its path relative to the build directory: a map of another directory left the root in "__FILE__" of the cached object, and the build directories at different depths, whose objects have different relative paths, shared the entries. Now the maps give the prefixes, that the compiler removes from all paths of the result - also of the debug info, if any - and only the forms of the root among them are replaced in the key. If none is, the sources of the root aren't shared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | d6020dc | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Check the shadowing of the headers from the own directories of the compiler The check of the shadowing knew only the include directories from the flags: a new file in an include directory with the name of a header from the standard library or /usr/include wasn't seen - the compiler would take it, and the cache returned the old object. Now the include directories, that the compiler searches by itself, are taken from its verbose preprocessing of nothing - once for the compiler, the language and the flags, that change them - and follow the system ones. The lookups of the direct cache take ~3 ms more for the sources of ydb. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 98fa2a6 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Show the used and the maximum size of the cache in the title of its group The outputs stored once for several entries move from "Saved" to the size of the cache: they save the space only compared to a cache without the deduplication - and a part of them are the same result under the key of the direct cache and of the preprocessed source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 761c01c | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Count only the links with several build directories as saved The first link of a cache file with a build directory only replaces the copy of the build: the space isn't saved compared to the build without the cache. The other links - e.g. with the builds of other working copies - save it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 95c17ed | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Don't show the estimate of the saved preprocessing Without the cache nothing is preprocessed: the estimate showed only the overhead of a cache, that preprocesses every source - not what the cache saves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 1fe24fc | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Count the removed entries, that are needed again The cache remembers the evicted and the forgotten entries - the "ghosts" in ghosts.sqlite - for ghosts_ttl (30 days by default). They are found by the hashes of their objects: when a new entry has the object of a removed one, the removed entry would have been a hit - the loss is counted with the time of the new compilation, and the record of the removal is dropped. For the evicted entries the loss keeps the size of the entries, that were evicted since it: how much larger cache would have kept it. A new group "Lost" in --stats: the losses to the eviction and to --forget-*, their time, how much larger cache would keep 50%, 90% and all of them, and the number of the remembered removals. --tag-stats shows the losses of every tag - also of the forgotten ones. --forget-removed forgets the records. Revision 3 of the format: the older daemons don't add the records. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 59fff79 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Show in the statistics, what the cache saves A new group "Saved" in --stats: - the compilation time of the entries, found by the direct and the simple hits - the manifest keeps it since the revision 2 of the format; - an estimate of the preprocessing time: the direct hits and the skipped preprocessings by the average time of a preprocessing; - the disk space of the copies, that aren't needed: of the outputs of several entries, and of the outputs linked with build directories; - the size of the outputs restored without writing: unchanged or linked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | d7e13c8 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Replace the extensions of the universal message with its fields Every message of the protocol was an extension of "net.proto.Universal", declared beside its own message in different files, and the code used the API of the extensions. Now "Universal" lists all of them as its fields - with the numbers of the old extensions, so the messages stay the same on the wire. Several of them still go together, e.g. a result with its status. The protos of the messages don't depend on "net/universal.proto" anymore - it depends on them. "Connection::SendAsync()" and "SendSync()" put the message into its field by its type. The protos stay proto2: proto3 has no "required" fields and no defaults in the format, and moving them into the code gives nothing here. "net/test.proto" and "connection_linux_test.cc" are removed: the test isn't built since the revival of the build, and it was the only user of the proto. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 2321e9d | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Drop the migrations and the old formats of the cache The cache migrated the manifests of the old versions, and read the entries of the old layouts: the outputs beside the manifests, compressed with Snappy or not. Now there is a generation of the format for such changes, and it's cheaper to rebuild the cache once, than to keep and test the code for every old format. Generation 2 of the cache: - the object and split DWARF files only in the blob store; - the manifests of other versions aren't migrated - they're removed as broken; the migrations of the manifests and of the index are gone; - the index keeps, if the entry is a manifest of the direct cache, and the hashes of its blobs: the references to the blobs are counted at the start without reading all manifests - it took about a minute on the cache of ydb; - Snappy and its submodule are gone; "compression: SNAPPY" in the configuration means zstd. The cache of the generation 1 is removed at the start. Only the files of the cache are removed now: the directory of the cache may have other files - e.g. when it's set to another directory by mistake - and the cache without the file "format" is of the generation 1 only, if it has the databases of the cache. The files of the cache are checked at the start only after a crash, or when the index is created anew: the file "running" exists, while the daemon uses the cache. After the check the index rows of the entries, that are gone, are removed too. The daemon stops normally on SIGTERM and SIGINT now: it blocks them in all threads and waits for them, saves the recent accesses to the index, removes "running", and exits without waiting for the running compilations - the clients compile without it. The compilers get the signals unblocked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 50676b0 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Keep the generation of the cache format on disk The format of the cache changes: the manifests, the blobs, the databases, the keys. The old formats of the manifests are migrated or read as is, but some changes can't be read by the older daemons, or by the newer ones - and a daemon couldn't tell, which cache it's given. Now the root of the cache has the file "format": the generation, the revision and the version of the daemon, that has written it. The generation changes with the incompatible changes; at the start the daemon: - removes the cache of an older generation: moves it aside at once - to "<cache>.removed-<time>-<pid>" - and removes it in background; the leftovers of the crashes are removed at the next start; - doesn't use the cache of a newer generation, and doesn't touch it: e.g. a newer daemon is used again later. It compiles without the cache, and reports CACHE_INCOMPATIBLE in the statistics - the log of the autostarted daemon isn't seen. The revision changes with the compatible changes - new optional fields, that the older daemons ignore: the daemons of the same generation use the cache, and the newer revision stays in the file. The cache without the file is of the first generation - the current one: the cache before this change is used as is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 8ba9b6e | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Hard-link a cache file to one place outside of the cache at most With linked outputs the same cache file could be hard-linked into several build directories - e.g. the worktrees with "source_roots" - and, after the deduplication, into several outputs of one build directory. If anything modified one of them in place - "cp" over an existing file, a tool, that keeps the hard links - the others changed too, with their modification time: the build systems don't expect it, and would link the changed object into other binaries. dist-clang checks the hashes only for its own hits. Also the restoration of a hit touched the modification time of the shared file, and the other build directories relinked their binaries. Now a hit links the cache file only if it isn't linked with any other place - except the restored output itself - and makes a copy otherwise (or a copy-on-write clone, where it's possible). If another task links the same file meanwhile, the link is replaced with a copy. "max_output_links" in the cache configuration sets the limit - 1 by default, 0 removes it. The store links the produced output with a new cache file only, so it doesn't add the links to the existing ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | c49467b | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Show the sizes of the tags, that are linked with build directories The size of a tag included the outputs, that are linked with build directories: they take no space in the cache, and "--forget-tag" doesn't remove them - the statistics didn't tell it. Now "--tag-stats" shows them in the column "in builds", and the column "freeable" shows, what the removal of the tag frees now - the same as the dry run of "--forget-tag": the exclusive entries without the linked outputs, and the outputs, that no other entry uses. The number of the exclusive entries isn't shown anymore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | c3c398e | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Tag the cache entries from the command line "dist-clang --tag=<tag> clang++ ..." tags the entries, that the compilation stores or finds in the cache - e.g. with the git branch of the sources. The tag may be repeated; the empty ones are ignored, so a command substitution in the launcher, that gives nothing, doesn't break the build. The client passes the tags to the daemon in DC_TAGS of its environment, as "--restat" does - it isn't a part of the key. The options of the launcher may go in any order now. With GN the command substitution goes into "cc_wrapper": GN escapes "$" for Ninja, the shell runs it for every compilation, and the command line stays the same for Ninja - a branch switch doesn't rebuild anything by itself. "dist-clang --tag-stats" shows the number and the size of the entries of every tag, the exclusive ones, and the sizes, shared by the pairs of tags. "dist-clang --forget-tag=<tag>" shows, what the removal of the tag would free, and removes it with "--force". The daemon handles both in the cleaner, after the recent accesses, and replies from there - the requests don't block the connections of the compilations. The manifests of the direct cache aren't tagged, and aren't counted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 2b5ba05 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Don't wait for the preprocessing on a miss of the direct cache On a miss of the direct cache the daemon preprocessed the source, looked for it in the simple cache, and only then compiled: the preprocessing took about a quarter of the compilation, and the compiler preprocessed the source again anyway. On ydb "util" (653 sources) the first build took 26.9 s instead of 21.0 s without the cache. Now "preprocessing" in the emitter configuration chooses, what to do: - ALWAYS: as before. - NEVER: compile at once, and store the result only for the direct cache - like the edited sources are compiled already. - WHEN_IDLE, by default: compile at once, and preprocess at the same time in the thread of the cache lookup, while the compilations and such preprocessings take less processes, than there are CPUs. If the preprocessed source is found in the simple cache first, the compilation is canceled, and the result is restored from the cache. Otherwise the result is stored with the hash of the preprocessed source too: by the compilation, if the hash is known before it ends, or by the preprocessing, after the result is stored. The sources, that weren't compiled with the same flags (except the preprocessing-only ones), aren't preprocessed at all - their preprocessed versions can't be in the cache. Without this the first build preprocessed most sources: the build doesn't keep all CPUs busy all the time. The edited sources aren't preprocessed, as before, and the compilations without the dependency files and all compilations with remotes are always preprocessed. The compilation is canceled with the new "Process::Cancel()" - an eventfd, that the running process waits for along with its outputs. The state of the race is in "Speculation": the canceled compilation doesn't store or report anything, the cancellation after the end of the compilation doesn't happen. ydb "util" from scratch: 22.1 s with WHEN_IDLE, 22.3 s with NEVER, 27.0 s with ALWAYS, 20.7 s without the cache; all hits: 1.4 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | bc48cd7 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Store the outputs once for all entries with the same contents Different keys often give the same object: other warning flags, a definition, that doesn't affect the source, a comment, that doesn't shift the lines. Each entry stored its own copy of the object and the split DWARF file. Now these files go into the blob store - "blobs/<2 chars>/<hash>" by the hash of their contents, the same one, that is checked on every hit. The manifest of a new entry has "blobs: true" and refers to the outputs by their hashes; its size doesn't include them. The entries of the old format are still read. The linked outputs are linked with the blob: a new output with the existing contents doesn't take any space in the cache. The copies are compressed in the blobs (".zst"); Snappy is used only for the old entries. The unused uncompressed blobs are compressed by the cleaner instead of the files of the entries - the compression of the old format is dropped. The references from the entries are counted in memory by the cleaner: at the start - from the manifests of all indexed entries, in background - and then for every stored and removed entry. A blob is removed with its last reference. After the counting at the start the blobs without references, older than the start, are removed: e.g. the blobs of the entries, that weren't stored because of a crash. A store may reuse a blob, which is removed right then - the entry is just not found later. The eviction counts every blob once: the removal of an entry frees its blobs only if no other entry uses them, and the entries with the blobs, linked with build directories, are kept as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 654106d | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Compress the cached outputs, that aren't linked anymore The linked outputs are stored uncompressed: they share the space with the build directories. But when the build directory gets another output - e.g. after a branch switch - the cache file becomes the only copy, and takes the full space, while it's likely not needed soon. Now the cleaner compresses such entries: the object and split DWARF files aren't linked with other places, and the entry wasn't used for "compress_after" seconds - an hour by default. The compression is the configured one, zstd by default. The hits of the compressed entries restore copies of the outputs. The files are replaced atomically, and then the manifest. A crash in the middle leaves a compressed file of an uncompressed entry: its hash doesn't match, and the entry is removed. The already compressed entries are skipped by the magic number of zstd, without loading the manifests. One pass compresses up to 1 GiB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 8844584 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Compress the cached files with zstd instead of Snappy Zstd at the fast level makes the object files about a quarter smaller than Snappy (ydb "util": 33% of the size instead of 45%), at 280 MB/s instead of 620 MB/s - still a few milliseconds per object. The split DWARF files, compressed with "-gz", don't shrink. The library is vendored as a submodule, v1.5.7, and is built without the assembly. The compression of an entry is stored in the new field "compression" of the manifest; the entries without it are read according to the old flag "snappy", which is still set for the Snappy entries - for the old versions of the daemon. The cache configuration gets "compression" - ZSTD by default; "snappy: false" still disables the compression, if there is no "compression". The linked outputs - the default for the emitter - stay uncompressed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 0741c43 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Verify a sample of cache hits by compiling them again A wrong cache hit is silent: if something, that affects the result, isn't a part of the key, the build gets the result of another compilation. Now the emitter can compile a percent of the hits again in background - "verify_hits_percent", off by default - and compare the object and split DWARF files with the cached ones. A mismatch is logged as an error, counted as HIT_VERIFICATION_FAILED, and the wrong entry is removed from the cache, so the next compilation isn't a hit; the matches are counted as HIT_VERIFIED. The verification doesn't touch the build directory: the outputs and the dependency file go into a temporary directory, and the object still refers to the split DWARF file by its original name ("-split-dwarf-file" with "-split-dwarf-output"). It runs one at a time with the lowest priority, and drops new hits, while 16 are waiting. If the inputs change after the hit, the result isn't compared. The compilations, that may record the command line in the debug info, aren't verified: the path of the output is another one. The driver arguments set "records_command_line" for them - by default with the debug info, since it depends on the build of the compiler (e.g. Ubuntu's clang records it). "-grecord-command-line" doesn't enable the debug info anymore. The removal of an entry goes through the cleaner thread, since it updates the index. Checked on ydb "util" with 100%: 38 hits verified, no failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 45f0368 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Detect new headers, that shadow the cached ones in the direct cache A direct cache entry is checked by the hashes of the headers, that were used by the compilation. A new file in an include directory, that is searched before the directory of a used header, would be found by the compiler instead - e.g. a generated header, or a header that appears on a branch switch - but the entry was still found, with the result of the old header. Now the daemon passes the include search directories to the cache, in the order of the search: the directory of the source, "-iquote", "-I", "-isystem" and "-idirafter" ones. For every header, that is under one of them, the same relative path in the previous directories must not exist. The include directive isn't known, so the header may be under several search directories, and some of these paths exist already - e.g. for "#include_next". Such paths, that exist after the compilation, are remembered in the variant of the manifest and aren't checked later. If one of them is created during the compilation, the compiler may have missed it, so the entry isn't stored. The implicit include directories of the compiler aren't checked, and neither are the directories of the including headers for the quoted includes - except the directory of the source. The lookup time on ydb "util" didn't change measurably: the paths are checked only for the variant, that matches the hashes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 2824a5f | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Check the integrity of all cache files and clean up after crashes The cache must never give a broken file to the build, whatever happens to the daemon or the system: - the manifest stores the hashes of all files of an entry - the object, the split DWARF, the dependency file and the diagnostics - and every hit checks them; an entry without the hashes is a miss; - a broken SQLite index is recreated on start: it's only an index, missing entries are just misses; - on start the daemon removes the temporary files, and the files of entries without the manifest - both are left by crashes in the middle of writing; - failures to update the index don't crash the daemon anymore. tools/stress/crash_test.py kills with SIGKILL the daemon, compilers, clients or ninja at random moments, completes the build and compares all objects with a build without the cache. 65 iterations passed without wrong objects; the previous version failed it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 1a868e4 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Don't cache results, if their inputs change during the compilation A fault-injection test (kill -9 of the daemon, compilers, clients and ninja at random moments, with sources reverted while the killed compilations may still run) found wrong objects in the build and missing objects: - A stale task, which client is gone, could still touch the output after the task of the next build: move it aside, put the previous version back, or delete it. - The direct cache entry is keyed by the source and headers, read after the compilation: if they changed during it - e.g. edited in the editor, or reverted - the result was stored under the key of the new contents. Now: - tasks with the same output are serialized, and each checks, that its client is still connected, before touching the output - so a stale task can only act before the task of the waiting client; - the time of the file system is taken, when a task starts; if the source, any included file or other input is modified after it, the result isn't stored in the cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | aa50f62 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Never write outputs for clients that are gone If a client was killed in the middle of a compilation - e.g. the build was interrupted - the daemon finished the compilation and wrote the outputs anyway. When the source was changed and rebuilt in the meantime, the stale compilation overwrote the new object and dependency file after the build had finished - and the build system took the wrong object as up to date. Reproduced with a slow source: the client is killed, the source is replaced, and rebuilt; ~10 seconds later the old object replaced the new one. The same happened, when the daemon itself was killed: its compiler lived on as an orphan. Now: - the compiler runs in its own process group, and is killed with the whole group, as soon as the client's socket is disconnected - before it renames its outputs into place; - the compiler gets SIGKILL, if the daemon dies (PR_SET_PDEATHSIG); - cache hits and remote results aren't written for disconnected clients; - the preprocessing writes the dependency file into a temporary directory, and the recheck of warnings doesn't write it at all: only the compilation, that the client waits for, writes into the build directory; - accepted sockets are close-on-exec atomically (accept4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | ac9a5f8 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Share the cache between checkouts of the same sources The same sources in different places - e.g. git worktrees - never shared the cache: the paths of the sources are in the flags, in the line markers of the preprocessed source and in the headers of the direct cache manifests. Now the cache config may list "source_roots". If the compiled source is under one of them, the root is replaced with a placeholder in the cache key - both as an absolute path and as a path relative to the current directory - in the flags and in the line markers. The direct cache manifests store the headers under the root relative to it, and resolve them against the root of the current compilation. This is done only for relocatable compilations, which results don't contain the paths: they use "-fmacro-prefix-map" (or "-ffile-prefix-map") for "__FILE__", and with the debug info also "-fdebug-prefix-map" and "-fdebug-compilation-dir". Otherwise the object files would contain the paths of another checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | a41b440 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Separate the flags of diagnostics from the cache key Any change of flags missed the cache, though some of them don't affect the object file at all. - The flags of the diagnostics format (colors, message length, caret, limits of notes, etc.) aren't a part of the key anymore. Since the cached diagnostics may be colored or not, the colors are stripped, unless the current command asks for them. - The warning flags are classified separately. They stay a part of the key by default, but with the emitter config "recheck_warnings" they don't: the entry remembers the hash of the warning flags, and a hit with other warnings runs the compiler with "-fsyntax-only" to get the actual diagnostics, while the object file is taken from the cache. If the check fails - e.g. with "-Werror" - its errors are reported, as the compilation would do. The backend warnings are missed then, so it's off by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 1ca7beb | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Don't preprocess edited sources on a cache miss On a direct cache miss the emitter preprocessed the source to look up the simple cache. For an edited source that's almost always a waste: the preprocessed source is new too, so the miss costs the preprocessing plus the full compilation - about 15% slower than without the cache. Now the cache remembers the last version of each source compiled with the same flags. If the direct cache misses, and the source differs from the remembered version, the emitter compiles it right away, and stores the result only for the direct cache - under a unique hash, that the simple cache never finds. If only the headers have changed, the emitter still preprocesses the source: then the simple cache catches touched or regenerated headers. The fast path requires a dependency file - to fill the direct cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | b2954b5 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Don't touch unchanged outputs for build systems that re-stat them The objects restored from the cache, or compiled again with the same result, were always rewritten - with the new modification time, so the build system relinked everything that depends on them. E.g. touching a header, or changing a comment in it, relinked all the libraries and binaries. Ninja can re-stat outputs after a command ("restat = 1") and skip dependents, if the output didn't change. Now the emitter supports it: - on a cache hit the output is kept, if it has the same contents already (the same inode as the cache file, or the same hash); - before a local compilation the previous outputs are moved aside, and put back, if the new ones are the same. It's enabled per command with "dist-clang --restat" (passed to the daemon as DC_RESTAT=1 in the environment), or for all commands with the emitter config "keep_unchanged_outputs". Without re-stating the outputs would look outdated forever, so it's off by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 6a67f3d | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Rework the eviction policy of the cache The cache was cleaned by the LRU order, counting all files. With the outputs linked into build directories this is wrong in both ways: removing such an entry doesn't free the disk space, and it's the most likely one to be used again. Also most of new entries are never used again - they are the results of one-off edits - while LRU keeps them as long as the useful ones. Now: - the index counts hits of entries; - only the files, that aren't hard-linked with other places, count against the size limit; the entries with shared outputs are never removed; - entries, that were never hit, are removed first, then the least recently used ones; the cleaning goes 10% below the limit, to happen less often; - entries are also removed, while the disk has less free space than "min_free_percent" of the cache config (5% by default); - the statistics show the own and shared sizes of the cache and the number of entries. The index table gets the "hits" column, existing indexes are migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | b3757f1 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Link outputs into and out of the cache instead of copying Copying object files to the cache on a miss and back on a hit costs disk space and I/O: a build directory and the cache store the same files twice. Now the emitter stores the outputs uncompressed and links them: - with a copy-on-write clone (FICLONE) on filesystems that support it; - with a hard link otherwise - the cache files are made read-only, so that tools writing to a hard-linked output in place fail loudly instead of corrupting the cache; - with a plain copy as the last resort, or for another owner of outputs. The manifest stores the hashes of the linked files, and every hit checks them: a file modified in place through another link is dropped from the cache. The restored outputs get the current mtime, so that the build system sees them newer than the sources. The outputs are always replaced atomically. The mode is on by default for emitters ("link_outputs" in the cache config) and turns off the Snappy compression; absorbers keep the old behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | 829ae74 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Keep several sets of headers per source in the direct cache The direct cache manifest of a source stored only the last set of included headers. When the same source includes different headers - e.g. after a branch switch, or a changed include in a header - the manifest was overwritten, and switching back missed the direct cache. Now the manifest keeps up to 8 most recent sets of headers, and the lookup tries all of them. The old manifests with a single set are still readable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | ccaf385 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Revive the build and replace the libclang-based client with a thin one The project hasn't been built since 2019: GN, third-party code and the bundled libclang 4.0 are hopelessly outdated. This change makes it build with the current toolchains and reworks the client-daemon split, so that the cache works with any recent Clang. Build: - support the current GN: no |set_sources_assignment_filter()|, no Python outputs of protoc; the compilers are set via "clang_cc"/"clang_cxx" args; - use the system C++ library and C++20, build own code as static libraries; - update submodules: protobuf v21.12, googletest v1.15.2, snappy 1.2.1 (the old Snappy corrupted object files when built with a modern Clang); - remove bundled LLVM/libclang, libc++, libc++abi, libunwind, gperftools, leveldb (the direct cache index is stored in SQLite now), gflags, and the deb/rpm/pkg packaging; - add GitHub Actions CI. Client and daemon: - "dist-clang" is a thin client: it works as a compiler launcher or through a symlink, sends argv, cwd and environment to the daemon, and runs the compiler as is if the daemon can't handle the command; it starts the daemon in background on demand; - "dist-clangd" classifies the flags of the compiler driver itself: unknown flags affect the cache key, unsupported commands are run as is; - the compiler identity is the hash of its binary and of its "-###" output; - the key includes the environment affecting the compilation, the current directory for the debug info, the split DWARF name and the contents of profiles, sanitizer lists and plugins; "-ftime-trace" doesn't affect it; - cache the split DWARF file, and restore the dependency file with the target of the current command; - don't store direct cache entries, if any header uses the time macros; - "dist-clang --stats" prints the statistics of the daemon. Tools: - tools/bench/incremental.py measures incremental rebuilds with and without the cache; - tools/check_args reports the commands of a compilation database, that can't be cached. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| Commit: | da235ad | |
|---|---|---|
| Author: | Ivan | |
| Committer: | GitHub | |
Save the arguments order (#124) Also upgrade `args_parser` to print generated command line from daemon
The documentation is generated from this commit.
| Commit: | ce556fe | |
|---|---|---|
| Author: | Matvey Larionov | |
| Committer: | abyss7 | |
Add support for -frewrite-includes flag while preprocessing (#116) * Added support for rewrite-includes flag while preprocessing * Added rewrite-macros to ignored flags * Fixed review issues * Fix typo in comment * Updated tests * Fix dead pointer
| Commit: | 9755135 | |
|---|---|---|
| Author: | Matvey Larionov | |
| Committer: | abyss7 | |
Implemented strict sharding (#110) A lot of changes in the |LockedQueue| logic
| Commit: | 67dca53 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Do not send "hash_match", if emitter doesn't provide "handled_hash".
| Commit: | 19429c8 | |
|---|---|---|
| Author: | Matvey Larionov | |
| Committer: | abyss7 | |
Calculate unhandled hash only once and verify hash on absorber (#111) * Calculate unhandled hash only once. Also verify hash on absorber * Added tests for cache/hash flags in absorber response
| Commit: | 74a43b3 | |
|---|---|---|
| Author: | Matvey Larionov | |
| Committer: | abyss7 | |
Support PCH & PTH files (#103) There are still some issues. Will fix them after this commit: * Don't render included headers in `FillFlags()` to remove duplication of code in tests and production. * `a call` → `calls` * Simplify and rename test `StoreDirectCacheForLocalResultAfterCacheMiss` → `StoreDirectCacheForLocalResultWithAndWithoutIncludedHeaders`. * Fix deps contents in tests to contain absolute header path instead of absolute PCH path.
| Commit: | fe21b4a | |
|---|---|---|
| Author: | Matvey Larionov | |
| Committer: | abyss7 | |
Add more metrics Also add more tests on `Emitter`
| Commit: | e6c3039 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Properly check and use the number of total shards. Total shards can't be less than 2.
| Commit: | c137b42 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Properly check and use the number of total shards. Total shards can't be less than 2.
| Commit: | 664c212 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Coordinator now sets total shards for emitter.
| Commit: | 44e9ad2 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Coordinator now sets total shards for emitter.
| Commit: | dbdf066 | |
|---|---|---|
| Author: | abyss7 | |
| Committer: | GitHub | |
Implement a sharding of tasks between remote hosts (#96) * Added distribution support to LockedQueue Now Emitter finds out a hash of tasks for remote execution and puts this info to LockedQueue. Also DoRemoteExecute now has assigned distribution value that is used to pop appropriate task from queue. * Factored Index class out of LockedQueue Also updated distribution/range wording to shard/shards_number. Clang version used in travis builds also updated. * Use hash of |HandledSource| to compute shard * Add check on generated source when using shards. * Calculate shard for task - using handled source. * Use a separate worker pool for coordinators. Otherwise they will hang forever. Also don't use progressive sleep in |DoPoll()|. * Fix CoordinatorNoConnection test. * Fix bug with reverse index in LockedQueue. * Added tests for LockedQueue<T>::Index (#97) * Remove task counting from |ThreadPool| interface. It was used only in tests. * Move |LockedQueue::Index| implementation into separate header. * Add pop timeout to |LockedQueue| interface. And take it from configuration in emitter. * Implement |LocketQueue::Pop()| that respects external pool shutdown. * Add more tests on Coordinator rotation logics. * Fix Coorditanor rotation tests. * Update QtCreator project. * Update 'total_shards' from coordinator. * Clean-up emitter tests. * Implement EmitterTest.ConfigurationUpdateFromCoordinator.
| Commit: | 8b6c752 | |
|---|---|---|
| Author: | Matvey Larionov | |
| Committer: | abyss7 | |
Implemented coordinator role. (#93) Also introduced changes to emitter to make him poll known coordinators to receive new remote hosts. Also added distribution field for remotes, but this one is not used for now.
| Commit: | a8b2dba | |
|---|---|---|
| Author: | Shekeen | |
| Committer: | abyss7 | |
Support sanitizer blacklist file (#70) * Support sanitize blacklist file * Fix tests compilation * Correct hash computation * Get rid of ExtraFile type, use Immutable instead. * Remove std::transform and std::accumulate calls. * Refactor hash computation * Review fixes * Use HashMap instead of List for extra files. * Move ExtraFiles type alias to FileCache, more tests. * Add more tests * Remove fsanitize-blacklist flag when preprocessing + one more test.
| Commit: | fc94d9c | |
|---|---|---|
| Author: | Andrey Malets | |
| Committer: | abyss7 | |
Issue 23: implement connection timeout (#62) * ISSUE-23: pass connect timeout to network service. * ISSUE-23: implement Linux part of connect with timeout. * ISSUE-23: implement Mac part. * ISSUE-23: enhance EndPoint a little, cover with test. * ISSUE-23: fix EndPoint::FromPassive and a test. * ISSUE-23: add a test for NetworkService::Connect. * ISSUE-23: fix EndPoint test, use dummy solution for NetworkServiceTest.ConnectTimedOut. * ISSUE-23: fix tests and timeout on Mac. * ISSUE-23: unify zero timeout with Mac, cleanup unused code. * ISSUE-23: s/indefinite/default/. * ISSUE-23: do not include a port if it is empty. * ISSUE-23: cleanup. * ISSUE-23: fix CHECK condition and move it to proper place. * ISSUE-23: fix buffer length for AF_INET6 address. * ISSUE-23: reorder parameters in code and protobufs. * ISSUE-23: remove tests and some EndPoint methods. * ISSUE-23: fix argument order, use clang-format. * ISSUE-23: fix confusing comment. * ISSUE-23: fixed compilation of clang/main.cc
| Commit: | 4fcc9c1 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Do store cache index by default.
| Commit: | b507c84 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Do store cache index by default.
| Commit: | f1e64a0 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
ISSUE-49: Take the process run timeout from config for Absorber.
| Commit: | 592bfa5 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Isolate client configuration logic in a separate class.
| Commit: | 020d645 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-28: implement 'disabled' option for the client.
| Commit: | b8fc880 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-32: Implement the storing of the file cache index on disk. No tests for now.
| Commit: | 0cd49e4 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-24: Store the size of an entry in the manifest v1.
| Commit: | 0a3d53c | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-24: implement |Migrator|.
| Commit: | 78cc5dc | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-14: Auto-generate C++ sources from proto files. Also a major refactoring is done. Need to fix packaging and python files auto-generation.
| Commit: | 92f7516 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Fix bug with the cache not get actually cleaned. DCHECK() -> DCHECK_O_EVAL()!
| Commit: | 8698a20 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-17: Add a couple of new cache size stats.
| Commit: | 8274352 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
ISSUE-6: Add more stats.
| Commit: | 9100c71 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-6: Implement the collector and the statistics service.
| Commit: | 30996ae | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-6: split |BaseDaemon| into |BaseDaemon| and |CompilationDaemon|. New |BaseDaemon| only does some networking stuff.
| Commit: | 1c57d1a | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Rename MACOS -> MACOSX
| Commit: | bdf07c4 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Load plugins only for an actual platform.
| Commit: | 80a9f43 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Use plugin path from the client configuration, if available.
| Commit: | 910f111 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Clean-up the cache periodically. Every 10 minutes by default.
| Commit: | f09de12 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Use the version of the internal Clang library for a direct hash.
| Commit: | b5eb9d3 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Allow to specify number of threads for the cache checking.
| Commit: | a95d842 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Specify default read/send timeout values if no config present.
| Commit: | 38bc170 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Make the read and send connection timeouts configurable.
| Commit: | 3090efe | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Totally remove the option |cache.sync| from everywhere.
| Commit: | 5b6d1b9 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Allow client to use configuration file instead of environment variables. Configuration file name should be ".distclang" It's searched in the current working directory and it's parents.
| Commit: | 912691c | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Turn off IPv6 by default. Since it doesn't work right now.
| Commit: | c88afc6 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Add support for IPv6.
| Commit: | 1d14592 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
Rename |file_cache| to |cache|. Also, move |FileCache| into namespace |cache|.
| Commit: | e97ff37 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
Update protobuf. They have moved to git repo - at last.
| Commit: | 8b71c81 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-19: Revert adding and using |hash| field. Also fix |RemoveEntry| test.
| Commit: | 0b98dd6 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-19: Use precalculated hash inside |LocalExecute| message. Also distinguish different string types: sources, hashes, etc.
| Commit: | afc5153 | |
|---|---|---|
| Author: | Ivan Lezhankin | |
| Committer: | Ivan Lezhankin | |
ISSUE-19: [WIP] Remove deprecated stuff. Also fix some bugs in Emitter.