Showing preview only (497K chars total). Download the full file or copy to clipboard to get everything.
Repository: sandstorm-io/ekam
Branch: master
Commit: 755967bace08
Files: 92
Total size: 470.6 KB
Directory structure:
gitextract_jfckbr5d/
├── .gitignore
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── LICENSE
├── Makefile
├── README.md
├── qtcreator/
│ ├── COPYING.txt
│ ├── EkamDashboard.json.in
│ ├── EkamDashboard.pluginspec.in
│ ├── ekamdashboard.pro
│ ├── ekamdashboard.qrc
│ ├── ekamdashboard_global.h
│ ├── ekamdashboardconstants.h
│ ├── ekamdashboardplugin.cpp
│ ├── ekamdashboardplugin.h
│ ├── ekamtreewidget.cpp
│ └── ekamtreewidget.h
├── src/
│ ├── base/
│ │ ├── Debug.cpp
│ │ ├── Debug.h
│ │ ├── Hash.cpp
│ │ ├── Hash.h
│ │ ├── OwnedPtr.cpp
│ │ ├── OwnedPtr.h
│ │ ├── Promise.cpp
│ │ ├── Promise.h
│ │ ├── Promise_test.cpp
│ │ ├── Table.h
│ │ ├── Table_test.cpp
│ │ ├── sha256.cpp
│ │ └── sha256.h
│ ├── ekam/
│ │ ├── Action.cpp
│ │ ├── Action.h
│ │ ├── ActionUtil.cpp
│ │ ├── ActionUtil.h
│ │ ├── ConsoleDashboard.cpp
│ │ ├── ConsoleDashboard.h
│ │ ├── CppActionFactory.cpp
│ │ ├── CppActionFactory.h
│ │ ├── Dashboard.cpp
│ │ ├── Dashboard.h
│ │ ├── Driver.cpp
│ │ ├── Driver.h
│ │ ├── ExecPluginActionFactory.cpp
│ │ ├── ExecPluginActionFactory.h
│ │ ├── MuxDashboard.cpp
│ │ ├── MuxDashboard.h
│ │ ├── ProtoDashboard.cpp
│ │ ├── ProtoDashboard.h
│ │ ├── SimpleDashboard.cpp
│ │ ├── SimpleDashboard.h
│ │ ├── Tag.cpp
│ │ ├── Tag.h
│ │ ├── dashboard.capnp
│ │ ├── ekam-client.cpp
│ │ ├── ekam-langserve.c++
│ │ ├── ekam.cpp
│ │ ├── ekam.ekam-manifest
│ │ ├── initNetworkDashboardStub.cpp
│ │ ├── langserve.capnp
│ │ └── rules/
│ │ ├── compile.ekam-rule
│ │ ├── include.ekam-rule
│ │ ├── install.ekam-rule
│ │ ├── intercept.c
│ │ ├── intercept.ekam-rule
│ │ ├── proto.ekam-rule
│ │ └── test.ekam-rule
│ └── os/
│ ├── ByteStream.cpp
│ ├── ByteStream.h
│ ├── DiskFile.cpp
│ ├── DiskFile.h
│ ├── EpollEventManager.cpp
│ ├── EpollEventManager.h
│ ├── EventGroup.cpp
│ ├── EventGroup.h
│ ├── EventManager.cpp
│ ├── EventManager.h
│ ├── File.cpp
│ ├── File.h
│ ├── KqueueEventManager.h
│ ├── OsHandle.cpp
│ ├── OsHandle.h
│ ├── PollEventManager.h
│ ├── Socket.cpp
│ ├── Socket.h
│ ├── Subprocess.cpp
│ └── Subprocess.h
└── vscode/
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── src/
│ └── extension.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
tmp
eclipse/bin
.cproject
.project
bin
lib
deps
================================================
FILE: CONTRIBUTING.md
================================================
## Contributing to Ekam
### Building
Use `make continuous` to build Ekam continuously and watch for changes. For LSP support in Visual Studio Code, run `make setup-vscode`.
`make continuous` requires you already have an install of Ekam. You can install Ekam by building it once (`make`) and then installing `bin/ekam{,-client,-langserve}` to a directory in PATH.
Note that `g++` tends to generate spurious warnings, so you may want to use `make continuous CXX=clang++` instead.
### Overview
Ekam has multiple components.
- `ekam`: This is the main binary. It actually builds the code, and uses capnp to communicate with the other binaries.
- `ekam-client`: This is a small tool to view the build output from `ekam`.
- `ekam-langserve`: This is a Language Server Protocol client that communicates with `ekam`.
- `ekam-bootstrap`: This is the same as `ekam`, but doesn't support the remote capnp protocol. As a result, some flags (like `-n`) aren't supported, and prebuilt capnp clients won't be able to connect.
`ekam-bootstrap` is built directly by `make`. All other components are built by `ekam-bootstrap`.
================================================
FILE: CONTRIBUTORS
================================================
The following people have made large code contributions to this repository.
Those contributions are copyright the respective authors and licensed by them
under the same Apache 2.0 license terms as the rest of the library.
Kenton Varda <kenton@sandstorm.io>: Primary Author
Google, Inc.: Employed Kenton when he wrote much of this code, and thus owns
copyright. Ekam was a 20% project as is not in any way endorsed by Google.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2015 Sandstorm Development Group, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
# Ekam Build System
# Author: Kenton Varda (kenton@sandstorm.io)
# Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
.SUFFIXES:
.PHONY: all install clean deps continuous setup-vscode
# You may override the following vars on the command line to suit
# your config.
CXX=g++
CXXFLAGS=-O2 -Wall
PARALLEL=$(shell nproc)
# for `make continuous`
EKAM=ekam
define color
@printf '\033[0;34m==== $1 ====\033[0m\n'
endef
all: bin/ekam
bin/ekam: bin/ekam-bootstrap | deps
$(call color,building ekam with ekam)
@rm -f bin/ekam
@CXX="$(CXX)" CXXFLAGS="-std=c++14 $(CXXFLAGS) -pthread" LIBS="-pthread" bin/ekam-bootstrap -j$(PARALLEL)
@test -e bin/ekam && printf "=====================================================\nSUCCESS\nOutput is at bin/ekam\n=====================================================\n"
# NOTE: Needs a full install of Ekam instead of the bootstrap so that LSP is available.
# To avoid recompiling 3 times, this requires the user to have Ekam already installed.
continuous:
$(call color,building ekam with ekam continuously)
@CXX="$(CXX)" CXXFLAGS="-std=c++14 $(CXXFLAGS) -pthread" LIBS="-pthread" $(EKAM) -j$(PARALLEL) -n :41315 -c
setup-vscode: vscode/vscode-ekam-0.2.0.vsix
code --install-extension $<
vscode/vscode-ekam-0.2.0.vsix:
cd vscode && npm install
cd vscode && npm run package
deps: deps/capnproto
deps/capnproto:
$(call color,downloading capnproto)
@mkdir -p deps
git clone https://github.com/capnproto/capnproto.git deps/capnproto
SOURCES=$(shell cd src; find base os ekam -name '*.cpp' | \
grep -v KqueueEventManager | grep -v PollEventManager | \
grep -v ProtoDashboard | grep -v ekam-client | grep -v _test)
HEADERS=$(shell find src/base src/os src/ekam -name '*.h')
# Use a subdirectory for bootstrapping so the object files don't overlap with the Ekam build.
OBJ_DIR := tmp/bootstrap
OBJECTS=$(addprefix $(OBJ_DIR)/, $(SOURCES:.cpp=.o))
$(OBJ_DIR)/%.o: src/%.cpp $(HEADERS)
@echo $(HEADERS)
@mkdir -p $(@D)
$(CXX) -Isrc -fPIC -std=c++14 -pthread -o $@ -c $<
bin/ekam-bootstrap: $(OBJECTS)
$(call color,compiling bootstrap ekam)
@mkdir -p bin
$(CXX) -Isrc -std=c++14 -pthread $(OBJECTS) -o $@
clean:
rm -rf bin lib tmp $(OBJ_DIR)
================================================
FILE: README.md
================================================
# Ekam Build System
Ekam ("make" backwards) is a build system which automatically figures out what to build and how to build it purely based on the source code. No separate "makefile" is needed.
Ekam works by exploration. For example, when it encounters a file ending in ".cpp", it tries to compile the file, intercepting system calls to find out its dependencies (e.g. included headers). If some of these are missing, Ekam continues to explore until it finds headers matching them. When Ekam builds an object file and discovers that it contains a "main" symbol, it tries to link it, searching for other object files to satisfy all symbol references therein. When Ekam sees a test, it runs the test, again intercepting system calls to dynamically discover what inputs the test needs (which may not have been built yet). Ekam can be extended to understand new file types by writing simple shell scripts telling it what to do with them.
Thus Ekam is, in fact, Make in reverse. Make starts by reading a Makefile, sees what executables it wants to build, then from there figures out what source files need to be compiled to link into them, then compiles them. Ekam starts by looking for source files to compile, then determines what executables it can build from them, and, in the end, might output a Makefile describing what it did.
Ekam is a work in progress.
## Building Ekam
### Warning
Ekam is an experimental project that is not ready for wide use.
That said, I (Kenton) have successfully used Ekam as my primary build system throughout the development of [Cap'n Proto](https://capnproto.org), [Sandstorm](https://sandstorm.io), and the [Cloudflare Workers](https://developers.cloudflare.com/workers/about/) runtime.
### Supported Platforms
At this time, Ekam only runs on Linux. It requires GCC 5+ or Clang 4+, as it uses C++14 features.
In the past, Ekam worked on FreeBSD and Max OSX, but the code to support that atrophied and was eventually deleted. Ekam uses a lot of OS-specific hacks and so is unlikely to work on any platform which is not explicitly supported.
We'd like to see other platforms supported, but Ekam's primary user and maintainer right now ([Sandstorm.io](https://sandstorm.io)) is itself highly Linux-specific, so there is not much pressure. (Let us know if you want to help.)
### Bootstrapping the build
Download and compile Ekam like so:
git clone https://github.com/capnproto/ekam.git
cd ekam
make
If successful, Ekam should have built itself, with the output binary at "bin/ekam".
Yes, we use make in order to bootstrap Ekam, mostly just because it's slightly nicer than a shell script.
### Compiling Ekam with Ekam
Compiling Ekam requires the GCC flag `-std=gnu++0x` to enable C++11 features, but currently there is no way for the code itself to specify compiler flags that it requires. You can only specify them via environment variable. So, to build Ekam with Ekam, type this command at the top of the Ekam repository:
CXXFLAGS=-std=gnu++0x ekam -j4
The `-j4` tells Ekam to run up to four tasks at once. You may want to adjust this number depending on how many CPU cores you have.
Note that Ekam looks for a directory called `src` within the current directory, and scans it for source code. The Ekam source repository is already set up with such a `src` subdirectory containing the Ekam code. You could, however, place the entire Ekam repository _inside_ some other directory called `src`, and then run Ekam from the directory above that, and it will still find the code. The Protocol Buffers instructions below will take advantage of this to create a directory tree containing both Ekam and protobufs.
Ekam places its output in siblings of `src` called `tmp` (for intermediate files), `bin` (for output binaries), `lib` (for output libraries, although currently Ekam doesn't support building libraries), etc. These are intended to model Unix directory tree conventions.
## Continuous Building
If you invoke Ekam with the `-c` option, it will watch the source tree for changes and rebuild derived files as needed. In this way, you can simply leave Ekam running while you work on your code, and get information about errors almost immediately on saving.
Note that continuous building is the only way to do incremental builds with Ekam -- any time you run a new Ekam process, it always starts from scratch. I generally just leave Ekam running in a console window 24/7.
## IDE plugins and other external clients
Ekam can, while running, export a network interface which allows other programs to query the state of the build, including receiving the task tree and error logs.
### Ekam Server
If you pass the `-n` flag to Ekam, it will listen for connections on a port and stream build status updates to anyone who connects. Invoke like:
ekam -n :41315
### Ekam Client
`ekam-client` is a very simple program that prints an Ekam build status stream to the console exactly as Ekam itself does. Currently `ekam-client` doesn't actually know how to create a network connection but instead reads the stream from standard input, so you can invoke it like this:
nc localhost 41315 | ekam-client
`ekam-client` is mostly just a tech demo, since it displays the same info that is already visible in the console where Ekam itself is running.
### Visual Studio Code Plugin
The `vscode` directory in your Ekam repository contains source code for a [Visual Studio Code](https://code.visualstudio.com/) plugin. [See its README for more info.](vscode/README.md)
### Qt Creator Plugin
The `qtcreator` directory in the Ekam repository contains source code for a [Qt Creator](http://qt-project.org/wiki/Category:Tools::QtCreator) plugin. Qt Creator is an open source C++ IDE that, despite its name, is quite nice even for non-Qt code.
To build the Qt Creator plugin, you'll want to download the Qt Creator source code release and build it from scratch. Then, go into the `qtcreator` directory in the Ekam repo and do the following:
export QTC_SOURCE=<path to qt creator source directory>
qmake
make
This should build the plugin and install it directly into your Qt Creator build.
Now you can start Qt Creator and choose the "Ekam Actions" view in the navigation frame. The plugin connects to a local Ekam instance running on port 41315; so, start ekam like this:
ekam -c -n :41315
The plugin will also add error markers to you source code, visible in the issue tracker. Double-clicking on a failed rule in the tree view will navigate the issues view to the first message from that action, which you can in turn use to navigate to the source location of the error.
## Using Ekam in your own code
### Preferred project layout
An Ekam project directory must contain a directory called `src` which contains all source code. Ekam will ignore everything other than this `src` directory.
Ekam will create sibling directories called `tmp`, `bin`, and `lib`; you probably shouldn't have any files in those directories to start, because it's nice to be able to `rm -rf` them to clean.
### Import the rule files
In order for Ekam to be able to build a project, it needs to find its rule files, which are under `src/ekam/rules` in Ekam's own source code. You will want to symlink this directory into your project's source tree somewhere. It does not have to be anywhere in particular, but `src/ekam-rules` is probably a good bet.
### Compiler flags
You can set the following environment variables to control how Ekam compiles your code:
* `CXX`: Sets the C++ compiler, e.g. `CXX=clang++`.
* `CXXFLAGS`: Sets C++ compilation flags, e.g. `CXXFLAGS=-std=c++11 -O2 -Wall`.
* `LIBS`: Sets linker flags, e.g. `LIBS=-lsodium -lz`
### Building binaries
When Ekam finds or compiles a `.o` file that defines the symbol `main`, it will attempt to link the file with other `.o` files defining all needed symbols (transitively) in order to produce a binary. The binary takes the name of the `.o` with the extension removed, e.g. `foo.o` produces a binary `foo`.
The binary is initially just dropped into the `tmp` directory, which mirrors the `src` directory. For example, `src/foo/bar.c++` will be compiled to `src/foo/bar.o`, which will link (if it defines `main`) into `src/foo/bar`. If you'd like for the binary to be output to the `bin` directory, you must declare a manifest file with the extension `.ekam-manifest`. This file is a simple text file where each line names a file and its output directory. For example:
bar bin
This says: "Once you've built `bar` (within this directory), copy it into `bin`."
You can also choose to rename the file when installing:
bar bin/baz
This says: "Once you've built `bar` (within this directory), copy it to `bin/baz`."
### Building libraries
Currently, Ekam does not support building libraries. This seems complicated to support since there's no automated way for Ekam to decide what makes a good library. You'd need to declare some set of modules representing the public interface, which is a bit sad.
For now, Ekam is suitable for projects where the final output is a binary. A "library" in Ekam is just a directory that contains some code. It does not build to a `.a` or `.so` file; instead, the objects may be directly linked into binaries found in other directories as needed.
### Handling Magic Singleton Registries
There is a common pattern in C++ code in which a particular file's symbols are not referenced from other files, but instead the file registers itself in some global registry at program startup which makes its functionality discoverable to the rest of the program.
By default, this pattern does not work with Ekam, because Ekam has no way to tell which of these magic self-registering files should be linked into which binaries, since their symbols are not referenced from other `.o` files.
To solve this problem, don't. This design pattern sucks and you should not use it. Rewrite your code so that its functionality is not dependent on what objects were specified on the link line. For example, have your `main()` function explicitly call `registerFoo()`, `registerBar()`, etc., for all the magic modules you want registered, ideally passing a registry object to each function so you don't need a singleton registry.
See [Singletons Considered Harmful](http://www.object-oriented-security.org/lets-argue/singletons) for extended discussion.
(We make a special exception for test frameworks, described below.)
### Debugging
If you want `gdb` to be able to find your code when debugging Ekam-built binaries, you should set up a couple symlinks as follows:
mkdir ekam-provider
ln -s ../src ekam-provider/canonical
ln -s ../src ekam-provider/c++header
To understand the reason for these symlinks, see the explanation of `intercept.so` later in this document.
### Tests
If Ekam builds a binary which ends in `-test` or `_test`, it will run that binary and report pass/fail depending on the exit status. Passing tests are marked in green in the output (whereas regular successful build actions are blue) in order to help you develop a pavlovian attachment to writing tests and seeing them pass.
Ekam has additional built-in support for two test frameworks: Google Test and KJ tests. When Ekam compiles a source file which declares test cases using one of these frameworks (e.g. using Google Test's `TEST` or `TEST_F` macros, or KJ's `KJ_TEST` macro) but without a `main` function, it will automatically link against the respective framework's test runner (which supplies `main`) in order to produce a test binary. Note that the detection of test declarations is based on linker symbols, not on scanning the source code, so don't worry if you've declared your own wrapper macros.
In order for Google Test or KJ test integration to work, the respective test framework's code must be in your source tree as a dependency (see below).
Note that tests are run with `intercept.so` injected, which has implications if your test does any filesystem access. See the explanation of `intercept.so` later in this document.
### Dependencies
If your project depends on other projects, and you want to build those other projects as part of your own build (rather than require the user to install the libraries on their system), you should follow the pattern that Ekam itself does.
The basic idea is to have a directory called `deps` into which you download the repositories of your external dependencies. Then, under `src`, place symlinks that deep-link into `deps` and pull out the code you want.
For example, to depend on Cap'n Proto, you would do something like:
mkdir deps
git clone https://github.com/sandstorm-io/capnproto.git deps/capnproto
ln -s ../deps/capnproto/c++/src/{kj,capnp} src
Note that `deps` should not be checked into your repository. Instead, you should provide a script that people run after cloning your repo that downloads dependencies. For Ekam's own build, we handle this in the Makefile, so you can look at that for an example. We may eventually add functionality to Ekam to handle dependencies so that you no longer need a Makefile for this.
**Now, here's the fun part:** If you work on a lot of different projects, instead of nesting dependencies as described above, you can symlink `deps` to `..`, such that all your git clones are siblings:
rm -rf deps
ln -s .. deps
You can do this with Ekam, for instance, if you happen to have Cap'n Proto cloned as a sibling to Ekam.
### Non-Ekam-clean Dependencies
If your dependency is not organized for Ekam, you might need to link to its top-level directory rather than a source subdirectory. For instance, Google Test separates its source code into `src` and `include` directories, so if you only link in its `src` you'll miss the headers. Instead, link in the whole repo like:
ln -s ../../gtest src
When Ekam sees a directory named `include`, it adds that directory to the include path for all other code it compiles. Thus the Google Test headers will end up includable by your code.
Note that projects imported this way will usually have a bunch of errors reported since they are not Ekam-clean. However, often (such as in the case of Google Test) the important parts will compile well enough to use.
### Per-directory compile options
To specify some additional compiler options that should apply within a particular directory (and all children, recursively), you may create a file called `compile.ekam-flags` and populate it with simple variable assignments in shell syntax. For example:
# Define FOO to 1 when compiling code in this directory.
CXXFLAGS=$CXXFLAGS -DFOO=1
`.ekam-flags` files are actually executed using `/bin/sh` just before invoking the compiler. When multiple flags files are in-scope, the flags file in the outermost directory runs first.
### Cross-compiling
Ekam currently supports cross-compiling to multiple target architectures at once, by listing additional (non-host) architectures in the `CROSS_TARGETS` environment variable:
CROSS_TARGETS="aarch64-linux-gnu" ekam
Notes:
* This is designed to work e.g. with the `crossbuild-essential-*` Debian packages.
* Ekam always compiles for the host architecture in addition to these targets. This is to support building tools like the Cap'n Proto compiler and then immediately using them in the same build.
* Ekam assumes the inter-object dependencies are the same on all targets. This tends to mean that it work well for targetting alternate CPU architectures, but not as well for targeting other operating systems.
* You may specify target-specific CXXFLAS and LIBS like `CXXFLAGS_aarch64_linux_gnu` and `LIBS_aarch64_linux_gnu`. If present, these completely replace the default `CXXFLAGS` and `LIBS`.
* If any unit tests are built, Ekam will try to use qemu to run them.
## Custom Rules
You may teach Ekam how to handle a new type of file -- or introduce a one-off build rule not triggered by any file -- by creating a rule file. A rule file is any executable with the extension `.ekam-rule`. Often, they are shell scripts, but this is not a requirement. Rule files can themselves be the output of other rules, so you could compile a C++ program that acts as a rule.
Ekam executes custom rules and then communicates with them on stdin/stdout using a simple line-based text protocol described below. The rule may also write error logs intended for the user to stderr.
When Ekam encounters an executable with the `.ekam-rule` extension, it first runs it with no arguments in order to "learn" it. At that time, the program may tell ekam what kinds of files it should trigger on using the `trigger` command (below). When Ekam encounters a trigger file, it will execute the rule again with the trigger file's canonical name as the argument. Alternatively, if you just want to implement a one-off action, then the rule may simply perform that action at "learn" time without registering any triggers.
### Canonical file names
A file's "canonical" name is the name without the `src/` or `tmp/` prefix. Thus canonical names do not distinguish between source files and build outputs. No two files can have the same canonical name -- it is an error for a build action to output a file which exists in the `src` directory, or for two actions to produce the same file under `tmp`.
You can ask Ekam to map a canonical name to a physical disk name using the `findInput` command.
### Tags
Ekam rules are triggered by tags. A tag is a simple string, conventionally of the format `type:value`. Tags are applied to files. For example, a `.o` file declaring the symbol `main` might be given the tag `c++symbol:main`. Rules may assign new tags to any file (source or output).
Ekam has some default rules that assign tags meant for broad use:
* `canonical:<filename>`: Each file receives this tag, where `<filename>` is its canonical name.
* `filetype:<extension>`: Each regular file that has a file type extension receives this tag, e.g. `filetype:.c++`.
* `directory:*`: Each directory receives this tag.
### Commands
A rule may perform the following commands by writing them to standard output.
* `trigger <tag>`: Used during the learning phase to tell Ekam that the rule should be executed on any file tagged with `<tag>`.
* `verb <text>`: Use during the learning phase to tell Ekam the rule's "verb", which is what is displayed to the user when the rule later runs. This should be a simple, descriptive word. For instance, for a C++ compile action, the verb is `compile`.
* `silent`: Use during the learning phase to indicate that when this command later runs, it should not be reported to the user unless it fails. Use this to reduce noise caused by very simple commands that perform trivial actions.
* `findInput <file>`: Obtains the canonical name of the given file. Ekam will reply by writing one line to the rule's standard input containing the full disk path of the file (e.g. including `src/` or `tmp/`). Ekam will remember that the build action depended on this file, so if the file changes, the action will be re-run. If no match was found, Ekam will return a blank line.
* `findProvider <tag>`: Find a file tagged with `<tag>`. If there are multiple matches, Ekam heuristically chooses the "preferred" one, which generally means the one closest in the directory tree to the file which triggered the rule. The path is returned as with `findInput`. Also as with `findInput`, the file is considered a dependency of the action. Ekam will re-run this action if the file changes *or* if the file Ekam chose to match `<tag>` changes.
* `findModifiers <name>`: Search for the file `<name>` in the trigger file's directory and every parent up to the source root. For each place that it is found (in order starting from the greatest ancestor), return the full disk path and mark it as an input. After returning all results, return a blank line to indicate the end of the list. This command is intended for finding "modifier" files which specify options that should apply within a particular directory. For instance, `compile.ekam-flags` is implemented this way.
* `noteInput <external-file>`: Tells Ekam that the action depends on `<external-file>`, which is a path outside of the project's source tree. For instance, `/usr/include/stdlib.h`. Currently Ekam ignores this, but in theory it could watch these files and re-run the action if they change.
* `newOutput <canonical-name>`: Create a new output file with the given canonical name. Ekam replies by writing the on-disk path where the file should be created to the rule's standard input.
* `provide <filename> <tag>`: Tag `<filename>` (a canonical name) with `<tag>`. The file must be a known input our output of this rule; i.e. it must have been the subeject of a previous call to `findInput`, `findProvider`, or `newOutput`.
* `install <filename> <location>`: Take the canonical filename `<filename>` and copy it to `<location>`, where `<location>` should start with `bin/`, `lib/`, etc.
* `passed`: Indicate that this action ran a test, and the test passed.
### `intercept.so`
Sometimes, it's hard to know what a build tool's exact inputs and outputs will be ahead of time. For instance, a C++ compiler run will need to input all of the header files `#include`ed by the source file. There's no reasonable way to know what these might be in advance, much less look up the locations of files to satisfy each.
To solve this, Ekam implements a library which can be injected into a tool via `LD_PRELOAD` in order to intercept filesystem calls, automatically issues the proper Ekam commands to register inputs and outputs, and then map the call to the real disk path.
The interceptor library is implemented in `src/ekam/rules/intercept.c` and built by `src/ekam/rules/intercept.ekam-rule`. You may request it by requesting the tag `special:ekam-interceptor`, which maps to the compiled `intercept.so`, which you may then inject into an arbitrary command using `LD_PRELOAD`. See `src/ekam/rules/compile.ekam-rule` to see how this is done with the C++ compiler.
When filesystem calls are being intercepted, an attempt to open a regular filename will be heuristically mapped to the closest file whose canonical name contains the full requested name as a suffix. For example, when processing `src/foo/bar/baz`, if the tool tries to open `qux/corge`, this could map to `src/foo/bar/qux/corge` or `src/qux/corge` or `src/grault/qux/corge`, but **not** `src/grault/corge` nor `src/qux/corge/grault` nor `src/corge`.
If you want to open a file by tag, the special virtual path `/ekam-provider/<tag-type>/<tag-value>` can be used. For example, since the `include.ekam-rule` rule tags all C++ header files with `c++header:<include-path>`, when we invoke the compiler using the inteceptor, we pass the flag `-I/ekam-provider/c++header`.
If you want to open a file purely by its whole canonical path (not using the heuristic that finds nearby files), you may do so by opening `/ekam-provider/canonical/<canonical-name>`, since as described above every file gets tagged with `canonical:<canonical-name>`.
## Get Involved
Have a question about Ekam, or want to contribute? Talk to us on the [Ekam discussion group](https://groups.google.com/group/ekam-tool).
Ekam is currently developed as part of the [Sandstorm.io](https://sandstorm.io) project and is primarily used by Sandstorm. If you like Ekam, consider [getting involved with Sandstorm](https://github.com/sandstorm-io/sandstorm/wiki/Get-Involved).
================================================
FILE: qtcreator/COPYING.txt
================================================
Code in this directory is licensed under Apache 2.0 like normal.
However, the icons under images/ are from the Qt Creator source code, and are thus
subject to Qt Creator's copyright and license. This probably means that the compiled
plugin cannot legally be redistributed because it combines incompatible licenses
(Apache vs. LGPL). I suppose I should either replace the icons or relicense the code.
Let me know if anyone actually cares.
================================================
FILE: qtcreator/EkamDashboard.json.in
================================================
{
\"Name\" : \"EkamDashboard\",
\"Version\" : \"0.0.1\",
\"CompatVersion\" : \"0.0.1\",
\"Vendor\" : \"KentonVarda\",
\"Copyright\" : \"(C) Kenton Varda and Google Inc.\",
\"License\" : \"Apache License v2.0\",
\"Category\" : \"Qt Creator\",
\"Description\" : \"Connects to the Ekam continuous build daemon and displays error markers.\",
\"Url\" : \"http://ekam.googlecode.com\",
$$dependencyList
}
================================================
FILE: qtcreator/EkamDashboard.pluginspec.in
================================================
<plugin name=\"EkamDashboard\" version=\"0.0.1\" compatVersion=\"0.0.1\">
<vendor>KentonVarda</vendor>
<copyright>(C) Google Inc.</copyright>
<license>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.</license>
<description>Connects to the Ekam continuous build daemon and displays error markers.</description>
<url>http://ekam.googlecode.com</url>
$$dependencyList
</plugin>
================================================
FILE: qtcreator/ekamdashboard.pro
================================================
# Ekam Build System
# Author: Kenton Varda (kenton@sandstorm.io)
# Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
QMAKE_CXXFLAGS += -std=c++11 `pkg-config --cflags capnp`
QMAKE_EXT_CPP += .c++
TARGET = EkamDashboard
TEMPLATE = lib
DEFINES += EKAMDASHBOARD_LIBRARY
# EkamDashboard files
SOURCES += ekamdashboardplugin.cpp \
ekamtreewidget.cpp
HEADERS += ekamdashboardplugin.h\
ekamdashboard_global.h\
ekamdashboardconstants.h \
ekamtreewidget.h
CAPNPS += dashboard.capnp
INCLUDEPATH += .
capnp_header.name = capnproto header
capnp_header.input = CAPNPS
capnp_header.output = ${QMAKE_FILE_BASE}.capnp.h
capnp_header.commands = \
capnp compile -oc++ --src-prefix=`dirname ${QMAKE_FILE_NAME}` ${QMAKE_FILE_NAME}; \
mv ${QMAKE_FILE_BASE}.capnp.c++ ${QMAKE_FILE_BASE}.capnp-noautolink.c++
capnp_header.variable_out = GENERATED_FILES
QMAKE_EXTRA_COMPILERS += capnp_header
capnp_src.name = capnproto src
capnp_src.input = CAPNPS
capnp_src.output = ${QMAKE_FILE_BASE}.capnp-noautolink.c++
capnp_src.depends = ${QMAKE_FILE_BASE}.capnp.h
capnp_src.commands = true
capnp_src.variable_out = GENERATED_SOURCES
QMAKE_EXTRA_COMPILERS += capnp_src
# Qt Creator linking
## set the QTC_SOURCE environment variable to override the setting here
QTCREATOR_SOURCES = $$(QTC_SOURCE)
isEmpty(QTCREATOR_SOURCES):error("Please set QTC_SOURCE to your Qt Creator source directory.")
## set the QTC_BUILD environment variable to override the setting here
IDE_BUILD_TREE = $$(QTC_BUILD)
isEmpty(IDE_BUILD_TREE):IDE_BUILD_TREE=$$(QTC_SOURCE)
## uncomment to build plugin into user config directory
## <localappdata>/plugins/<ideversion>
## where <localappdata> is e.g.
## "%LOCALAPPDATA%\Nokia\qtcreator" on Windows Vista and later
## "$XDG_DATA_HOME/Nokia/qtcreator" or "~/.local/share/data/Nokia/qtcreator" on Linux
## "~/Library/Application Support/Nokia/Qt Creator" on Mac
# USE_USER_DESTDIR = yes
PROVIDER = KentonVarda
###### If the plugin can be depended upon by other plugins, this code needs to be outsourced to
###### <dirname>_dependencies.pri, where <dirname> is the name of the directory containing the
###### plugin's sources.
QTC_PLUGIN_NAME = EkamDashboard
QTC_LIB_DEPENDS += \
# nothing here at this time
QTC_PLUGIN_DEPENDS += \
coreplugin \
projectexplorer \
cpptools \
texteditor
QTC_PLUGIN_RECOMMENDS += \
# optional plugin dependencies. nothing here at this time
###### End _dependencies.pri contents ######
QT += network
include($$QTCREATOR_SOURCES/src/qtcreatorplugin.pri)
LIBS += -L$$IDE_PLUGIN_PATH/Nokia `pkg-config --libs capnp-rpc`
RESOURCES += \
ekamdashboard.qrc
================================================
FILE: qtcreator/ekamdashboard.qrc
================================================
<RCC>
<qresource prefix="/ekamdashboard">
<file>images/state-blocked.png</file>
<file>images/state-deleted.png</file>
<file>images/state-done.png</file>
<file>images/state-failed.png</file>
<file>images/state-passed.png</file>
<file>images/state-pending.png</file>
<file>images/state-running.png</file>
<file>images/dir.png</file>
<file>images/dir-blocked.png</file>
<file>images/dir-deleted.png</file>
<file>images/dir-done.png</file>
<file>images/dir-failed.png</file>
<file>images/dir-passed.png</file>
<file>images/dir-pending.png</file>
<file>images/dir-running.png</file>
</qresource>
</RCC>
================================================
FILE: qtcreator/ekamdashboard_global.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EKAMDASHBOARD_GLOBAL_H
#define EKAMDASHBOARD_GLOBAL_H
#include <QtGlobal>
#if defined(EKAMDASHBOARD_LIBRARY)
# define EKAMDASHBOARDSHARED_EXPORT Q_DECL_EXPORT
#else
# define EKAMDASHBOARDSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // EKAMDASHBOARD_GLOBAL_H
================================================
FILE: qtcreator/ekamdashboardconstants.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EKAMDASHBOARDCONSTANTS_H
#define EKAMDASHBOARDCONSTANTS_H
namespace EkamDashboard {
namespace Constants {
const char ACTION_ID[] = "EkamDashboard.Action";
const char MENU_ID[] = "EkamDashboard.Menu";
const char TASK_CATEGORY_ID[] = "EkamDashboard.Menu";
} // namespace EkamDashboard
} // namespace Constants
#endif // EKAMDASHBOARDCONSTANTS_H
================================================
FILE: qtcreator/ekamdashboardplugin.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ekamdashboardplugin.h"
#include "ekamdashboardconstants.h"
#include "ekamtreewidget.h"
#include <capnp/serialize-async.h>
#include <kj/debug.h>
#include <deque>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/taskhub.h>
#include <QAction>
#include <QMessageBox>
#include <QMainWindow>
#include <QMenu>
#include <QDebug>
#include <QTimer>
#include <QFile>
#include <QRegExp>
#include <QtPlugin>
namespace EkamDashboard {
namespace Internal {
QString toQString(kj::ArrayPtr<const char> str) {
return QString::fromUtf8(str.begin(), str.size());
}
class EkamDashboardPlugin::FakeAsyncInput final: public kj::AsyncInputStream {
public:
kj::Promise<size_t> read(void* buffer, size_t minBytes, size_t maxBytes) override {
return tryRead(buffer, minBytes, maxBytes).then([=](size_t result) {
KJ_REQUIRE(result >= minBytes, "Premature EOF") {
// Pretend we read zeros from the input.
memset(reinterpret_cast<kj::byte*>(buffer) + result, 0, minBytes - result);
return minBytes;
}
return result;
});
}
kj::Promise<size_t> tryRead(void* buffer, size_t minBytes, size_t maxBytes) override {
Request request(buffer, minBytes, maxBytes);
if (byteQueue.size() > 0) {
KJ_IF_MAYBE(size, request.consumeFrom(byteQueue)) {
return *size;
}
}
requests.push_back(kj::mv(request));
return requests.back().finishLater();
}
void add(QByteArray bytes) {
while (!requests.empty()) {
auto& request = requests.front();
KJ_IF_MAYBE(size, request.consumeFrom(bytes)) {
request.fulfill(*size);
requests.pop_front();
} else {
// Not enough bytes to satisfy the request.
return;
}
}
byteQueue.append(bytes);
}
private:
class Request {
public:
Request(void* buffer, size_t minBytes, size_t maxBytes)
: pos(reinterpret_cast<kj::byte*>(buffer)),
minLeft(minBytes), maxLeft(maxBytes),
alreadyRead(0) {}
kj::Maybe<size_t> consumeFrom(QByteArray& bytes) {
size_t n = kj::min(maxLeft, bytes.size());
memcpy(pos, bytes.data(), n);
bytes.remove(0, n);
if (n >= minLeft) {
return alreadyRead + n;
} else {
pos += n;
minLeft -= n;
maxLeft -= n;
alreadyRead += n;
return nullptr;
}
}
kj::Promise<size_t> finishLater() {
auto paf = kj::newPromiseAndFulfiller<size_t>();
fulfiller = kj::mv(paf.fulfiller);
return kj::mv(paf.promise);
}
void fulfill(size_t amount) {
fulfiller->fulfill(kj::mv(amount));
}
private:
kj::byte* pos;
size_t minLeft;
size_t maxLeft;
size_t alreadyRead;
kj::Own<kj::PromiseFulfiller<size_t>> fulfiller;
};
std::deque<Request> requests;
QByteArray byteQueue;
};
EkamDashboardPlugin::EkamDashboardPlugin()
: hub(0), socket(0), seenHeader(false), waitScope(eventLoop),
fakeInput(kj::heap<FakeAsyncInput>()), readTask(nullptr) {}
EkamDashboardPlugin::~EkamDashboardPlugin() noexcept {
// Unregister objects from the plugin manager's object pool
// Delete members
}
bool EkamDashboardPlugin::initialize(const QStringList &arguments, QString *errorString) {
// Register objects in the plugin manager's object pool
// Load settings
// Add actions to menus
// Connect to other plugins' signals
// In the initialize method, a plugin can be sure that the plugins it
// depends on have initialized their members.
Q_UNUSED(arguments)
Q_UNUSED(errorString)
Core::ActionManager *am = Core::ActionManager::instance();
QAction *action = new QAction(tr("EkamDashboard action"), this);
Core::Command *cmd = am->registerAction(action, Constants::ACTION_ID,
Core::Context(Core::Constants::C_GLOBAL));
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
connect(action, SIGNAL(triggered()), this, SLOT(triggerAction()));
Core::ActionContainer *menu = am->createMenu(Constants::MENU_ID);
menu->menu()->setTitle(tr("EkamDashboard"));
menu->addAction(cmd);
am->actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
addAutoReleasedObject(new EkamTreeWidgetFactory(this));
return true;
}
void EkamDashboardPlugin::extensionsInitialized() {
// Retrieve objects from the plugin manager's object pool
// In the extensionsInitialized method, a plugin can be sure that all
// plugins that depend on it are completely initialized.
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
hub = pm->getObject<ProjectExplorer::TaskHub>();
hub->addCategory(Core::Id(Constants::TASK_CATEGORY_ID), QLatin1String("Ekam task"));
socket = new QTcpSocket(this);
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(socket, SIGNAL(readyRead()), this, SLOT(socketReady()));
tryConnect();
}
ExtensionSystem::IPlugin::ShutdownFlag EkamDashboardPlugin::aboutToShutdown() {
// Save settings
// Disconnect from signals that are not needed during shutdown
// Hide UI (if you add UI that is not in the main window directly)
delete socket;
socket = 0;
clearActions();
return SynchronousShutdown;
}
void EkamDashboardPlugin::triggerAction() {
// qDebug() << "triggerAction() qdebug";
QMessageBox::information(Core::ICore::mainWindow(),
tr("Action triggered"),
tr("This is an action from EkamDashboard."));
hub->addTask(ProjectExplorer::Task(
ProjectExplorer::Task::Error, QLatin1String("test error"),
Utils::FileName::fromUserInput(QLatin1String("/home/kenton/code/src/base/OwnedPtr.h")), 10,
Core::Id(Constants::TASK_CATEGORY_ID)));
}
void EkamDashboardPlugin::socketError(QAbstractSocket::SocketError error) {
// qDebug() << "Socket error: " << error;
reset();
}
void EkamDashboardPlugin::reset() {
if (!resetting) {
resetting = true;
clearActions();
QTimer::singleShot(5000, this, SLOT(retryConnection()));
}
}
void EkamDashboardPlugin::retryConnection() {
resetting = false;
// Cancel any async parsing still happening.
readTask = nullptr;
// Reset the fake input to clear out its buffers.
fakeInput = kj::heap<FakeAsyncInput>();
tryConnect();
}
void EkamDashboardPlugin::socketReady() {
fakeInput->add(socket->readAll());
eventLoop.run();
}
void EkamDashboardPlugin::tryConnect() {
seenHeader = false;
// qDebug() << "Trying to connect...";
socket->connectToHost(QLatin1String("localhost"), 41315);
readTask = messageLoop().eagerlyEvaluate(
[this](kj::Exception&& e){ KJ_LOG(ERROR, e); reset(); });
}
kj::Promise<void> EkamDashboardPlugin::messageLoop() {
return capnp::readMessage(*fakeInput).then([this](kj::Own<capnp::MessageReader>&& message) {
if (!seenHeader) {
seenHeader = true;
ekam::proto::Header::Reader header = message->getRoot<ekam::proto::Header>();
// qDebug() << "Received header: " << kj::str(header).cStr();
projectRoot = toQString(header.getProjectRoot());
} else {
ekam::proto::TaskUpdate::Reader update = message->getRoot<ekam::proto::TaskUpdate>();
// qDebug() << "Received task update: " << kj::str(update).cStr();
ActionState*& slot = actions[update.getId()];
if (slot == 0) {
slot = new ActionState(this, update);
} else {
slot->applyUpdate(update);
}
if (slot->isDead()) {
delete slot;
actions.remove(update.getId());
}
}
return messageLoop();
});
}
QString EkamDashboardPlugin::findFile(const QString& canonicalPath) {
QString srcpath = projectRoot + QLatin1String("/src/") + canonicalPath;
QString tmppath = projectRoot + QLatin1String("/tmp/") + canonicalPath;
if (QFile::exists(srcpath)) {
return srcpath;
} else if (QFile::exists(tmppath)) {
return tmppath;
} else {
return canonicalPath;
}
}
QList<ActionState*> EkamDashboardPlugin::allActions() {
QList<ActionState*> result;
foreach (ActionState* action, actions) {
if (!action->isHidden()) {
result << action;
}
}
return result;
}
void EkamDashboardPlugin::clearActions() {
foreach (ActionState* action, actions) {
delete action;
}
actions.clear();
}
// =======================================================================================
ActionState::ActionState(
EkamDashboardPlugin *plugin, ekam::proto::TaskUpdate::Reader initialUpdate)
: plugin(plugin), state(initialUpdate.getState()),
verb(toQString(initialUpdate.getVerb())),
noun(toQString(initialUpdate.getNoun())),
path(plugin->findFile(noun)),
silent(initialUpdate.getSilent()) {
auto log = initialUpdate.getLog();
if (log != nullptr) {
consumeLog(log);
}
if (!isHidden()) {
plugin->unhideAction(this);
}
}
ActionState::~ActionState() noexcept {
emit removed();
clearTasks();
}
void ActionState::applyUpdate(ekam::proto::TaskUpdate::Reader update) {
if (update.getState() != ekam::proto::TaskUpdate::State::UNCHANGED &&
update.getState() != state) {
bool wasHidden = isHidden();
// Invalidate log when the task is deleted or it is re-running or scheduled to re-run.
if (update.getState() == ekam::proto::TaskUpdate::State::PENDING ||
update.getState() == ekam::proto::TaskUpdate::State::RUNNING ||
update.getState() == ekam::proto::TaskUpdate::State::DELETED) {
clearTasks();
}
state = update.getState();
if (isHidden()) {
if (wasHidden) {
emit removed();
}
} else {
if (wasHidden) {
plugin->unhideAction(this);
} else {
emit stateChanged(state);
}
}
}
auto log = update.getLog();
if (log != nullptr) {
consumeLog(log);
}
if (state != ekam::proto::TaskUpdate::State::RUNNING && !leftoverLog.empty()) {
parseLogLine(toQString(leftoverLog));
leftoverLog.resize(0);
}
}
void ActionState::clearTasks() {
emit clearedTasks();
foreach (const ProjectExplorer::Task& task, tasks) {
plugin->taskHub()->removeTask(task);
}
tasks.clear();
leftoverLog.resize(0);
}
void ActionState::consumeLog(kj::StringPtr log) {
while (true) {
KJ_IF_MAYBE(pos, log.findFirst('\n')) {
leftoverLog.addAll(log.begin(), log.begin() + *pos);
log = log.slice(*pos + 1);
parseLogLine(toQString(leftoverLog));
leftoverLog.resize(0);
} else {
leftoverLog.addAll(log);
return;
}
}
}
void ActionState::parseLogLine(QString line) {
if (tasks.size() > 100) return; // avoid performance problems with too many tasks
static const QRegExp FILE(QLatin1String("^([^ :]+):(.*)"));
static const QRegExp INDEX(QLatin1String("^([0-9]+):(.*)"));
static const QRegExp WARNING(QLatin1String("(.*[^a-zA-Z0-9])?warning:(.*)"), Qt::CaseInsensitive);
static const QRegExp ERROR(QLatin1String("(.*[^a-zA-Z0-9])?error:(.*)"), Qt::CaseInsensitive);
static const QRegExp FAILURE(QLatin1String(" *failure *"), Qt::CaseInsensitive);
static const QRegExp FULL_LOG(QLatin1String("full log: tmp/(.*)"));
ProjectExplorer::Task::TaskType type = ProjectExplorer::Task::Unknown;
QString file;
int lineNo = -1;
int columnNo = -1;
// OMGWTF matching a QRegExp modifies the QRegExp object rather than returning some sort of match
// object, so we must make copies. Hopefully using the copy constructor rather than constructing
// directly from the pattern strings means they won't be re-compiled every time.
QRegExp fullLog = FULL_LOG;
QRegExp fileRe = FILE;
if (fullLog.exactMatch(line)) {
file = fullLog.capturedTexts()[1];
file = plugin->findFile(file);
if (!file.startsWith(QLatin1Char('/'))) {
file.clear();
}
} else if (fileRe.exactMatch(line)) {
file = fileRe.capturedTexts()[1];
if (file.startsWith(QLatin1String("/ekam-provider/c++header/"))) {
file.remove(0, strlen("/ekam-provider/c++header/"));
} else if (file.startsWith(QLatin1String("/ekam-provider/canonical/"))) {
file.remove(0, strlen("/ekam-provider/canonical/"));
}
file = plugin->findFile(file);
if (file.startsWith(QLatin1Char('/'))) {
line = fileRe.capturedTexts()[2];
} else {
// Failed to find the file on disk. Maybe it's not actually a file. Leave it in the error
// text.
file.clear();
}
}
QRegExp indexRe = INDEX;
if (indexRe.exactMatch(line)) {
type = ProjectExplorer::Task::Unknown;
lineNo = indexRe.capturedTexts()[1].toInt();
line = indexRe.capturedTexts()[2];
if (indexRe.exactMatch(line)) {
columnNo = indexRe.capturedTexts()[1].toInt();
line = indexRe.capturedTexts()[2];
}
}
QRegExp errorRe = ERROR;
QRegExp warningRe = WARNING;
QRegExp failureRe = FAILURE;
if (errorRe.exactMatch(line)) {
type = ProjectExplorer::Task::Error;
} else if (warningRe.exactMatch(line)) {
type = ProjectExplorer::Task::Warning;
} else if (failureRe.exactMatch(line)) {
type = ProjectExplorer::Task::Error;
}
// Qt Creator tasks don't support column numbers, so add it back into the error text.
if (columnNo != -1) {
line = QLatin1String("(col ") + QString::number(columnNo) + QLatin1String(") ") + line;
}
tasks.append(ProjectExplorer::Task(type, line, Utils::FileName::fromUserInput(file), lineNo,
Core::Id(Constants::TASK_CATEGORY_ID)));
plugin->taskHub()->addTask(tasks.back());
emit addedTask(tasks.back());
}
} // namespace Internal
} // namespace EkamDashboard
================================================
FILE: qtcreator/ekamdashboardplugin.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EKAMDASHBOARD_H
#define EKAMDASHBOARD_H
#include "ekamdashboard_global.h"
#include <extensionsystem/iplugin.h>
#include <coreplugin/icore.h>
#include <coreplugin/icontext.h>
#include <coreplugin/idocument.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/coreconstants.h>
#include <projectexplorer/task.h>
#include <QtNetwork/QTcpSocket>
#include <QByteArray>
#include <QHash>
#include <QList>
#include <kj/vector.h>
#include <kj/async.h>
#include "dashboard.capnp.h"
namespace ProjectExplorer {
class TaskHub;
}
namespace EkamDashboard {
namespace Internal {
class EkamDashboardPlugin;
class ActionState: public QObject {
Q_OBJECT
public:
ActionState(EkamDashboardPlugin* plugin, ekam::proto::TaskUpdate::Reader initialUpdate);
~ActionState() noexcept;
// Returns true if the action went from silent to non-silent, and thus a newAction event should
// be fired.
void applyUpdate(ekam::proto::TaskUpdate::Reader update);
bool isDead() {
return state == ekam::proto::TaskUpdate::State::DELETED;
}
ekam::proto::TaskUpdate::State getState() { return state; }
const QString& getVerb() { return verb; }
const QString& getNoun() { return noun; }
const QString& getPath() { return path; }
bool isHidden() {
return silent && state != ekam::proto::TaskUpdate::State::FAILED;
}
ProjectExplorer::Task* firstTask() { return tasks.empty() ? 0 : &tasks.first(); }
signals:
void removed();
void stateChanged(ekam::proto::TaskUpdate::State state);
void clearedTasks();
void addedTask(const ProjectExplorer::Task& task);
private:
EkamDashboardPlugin* plugin;
ekam::proto::TaskUpdate::State state;
QString verb;
QString noun;
QString path;
bool silent;
kj::Vector<char> leftoverLog;
QList<ProjectExplorer::Task> tasks;
void clearTasks();
void consumeLog(kj::StringPtr log);
void parseLogLine(QString line);
};
class EkamDashboardPlugin : public ExtensionSystem::IPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "EkamDashboard.json")
public:
EkamDashboardPlugin();
~EkamDashboardPlugin() noexcept;
bool initialize(const QStringList &arguments, QString *errorString);
void extensionsInitialized();
ShutdownFlag aboutToShutdown();
ProjectExplorer::TaskHub* taskHub() { return hub; }
QString findFile(const QString& canonicalPath);
QList<ActionState*> allActions();
void unhideAction(ActionState* action) {
emit newAction(action);
}
signals:
void newAction(ActionState* action);
private slots:
void triggerAction();
void socketError(QAbstractSocket::SocketError);
void retryConnection();
void socketReady();
private:
class FakeAsyncInput;
ProjectExplorer::TaskHub* hub;
QTcpSocket* socket;
bool seenHeader;
QString projectRoot;
QHash<int, ActionState*> actions;
kj::EventLoop eventLoop;
kj::WaitScope waitScope;
kj::Own<FakeAsyncInput> fakeInput;
kj::Promise<void> readTask;
bool resetting = false;
void reset();
void tryConnect();
kj::Promise<void> messageLoop();
void clearActions();
};
} // namespace Internal
} // namespace EkamDashboard
#endif // EKAMDASHBOARD_H
================================================
FILE: qtcreator/ekamtreewidget.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ekamtreewidget.h"
#include "ekamdashboardplugin.h"
#include <QVBoxLayout>
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/taskhub.h>
#include <utils/navigationtreeview.h>
namespace EkamDashboard {
namespace Internal {
static constexpr ekam::proto::TaskUpdate::State DEFAULT_STATE =
ekam::proto::TaskUpdate::State::UNCHANGED;
// This defines the priority ordering of states. The state (and icon) for a directory will be
// chosen based on the highest-priority state of its children.
static const ekam::proto::TaskUpdate::State ORDERED_STATES[] = {
DEFAULT_STATE,
ekam::proto::TaskUpdate::State::DELETED,
ekam::proto::TaskUpdate::State::DONE,
ekam::proto::TaskUpdate::State::PASSED,
ekam::proto::TaskUpdate::State::FAILED,
ekam::proto::TaskUpdate::State::PENDING,
ekam::proto::TaskUpdate::State::BLOCKED,
ekam::proto::TaskUpdate::State::RUNNING,
};
static int STATE_PRIORITIES[16];
struct StatePrioritiesInitializer {
StatePrioritiesInitializer() {
for (size_t i = 0; i < (sizeof(STATE_PRIORITIES) / sizeof(STATE_PRIORITIES[0])); i++) {
STATE_PRIORITIES[i] = -1;
}
for (size_t i = 0; i < (sizeof(ORDERED_STATES) / sizeof(ORDERED_STATES[0])); i++) {
STATE_PRIORITIES[static_cast<uint>(ORDERED_STATES[i])] = i;
}
}
};
static StatePrioritiesInitializer statePrioritiesInitializer;
// =======================================================================================
EkamTreeNode::EkamTreeNode(EkamTreeModel* tree)
: QObject(tree), tree(tree), isDirectory(true), parentNode(0), action(0), state(DEFAULT_STATE) {}
EkamTreeNode::EkamTreeNode(EkamTreeNode* parent, const QString& name, bool isDirectory)
: QObject(parent), tree(parent->tree), isDirectory(isDirectory), name(name),
parentNode(parent), action(0), state(DEFAULT_STATE) {}
EkamTreeNode::~EkamTreeNode() {}
int EkamTreeNode::row() {
if (this == tree->root) {
return -1;
}
int result = parentNode->childNodes.indexOf(this);
if (result == -1) {
qWarning() << "parentNode->childNodes doesn't contain this?";
}
return result;
}
QModelIndex EkamTreeNode::index() {
if (this == tree->root) {
return QModelIndex();
}
int rowNum = row();
if (rowNum == -1) {
return QModelIndex();
}
return tree->createIndex(rowNum, 0, this);
}
void EkamTreeNode::actionStateChanged(ekam::proto::TaskUpdate::State newState) {
stateChanged(newState);
}
void EkamTreeNode::actionRemoved() {
setAction(0);
if (parentNode != 0) {
parentNode->removeChild(this);
}
// QModelIndex myIndex = index();
// emit tree->dataChanged(myIndex, myIndex);
}
void EkamTreeNode::createNode(const QString& noun, const QString& verb, ActionState* action) {
int slash = noun.indexOf(QLatin1Char('/'));
QString childName = (slash == -1) ? QString(QLatin1String("%1 (%2)")).arg(noun, verb) : noun.mid(0, slash);
QList<EkamTreeNode*>::iterator iter = childNodes.begin();
while (iter < childNodes.end() && (*iter)->name < childName) {
++iter;
}
EkamTreeNode* selectedNode;
bool isNew = iter == childNodes.end() || (*iter)->name != childName;
if (isNew) {
QModelIndex i = index();
int r = iter - childNodes.begin();
tree->beginInsertRows(i, r, r);
selectedNode = new EkamTreeNode(this, childName, slash != -1);
childNodes.insert(iter, selectedNode);
} else {
selectedNode = *iter;
}
if (slash == -1) {
selectedNode->setAction(action);
} else {
selectedNode->createNode(noun.right(noun.size() - slash - 1), verb, action);
}
if (isNew) {
tree->endInsertRows();
} else {
QModelIndex nodeIndex = selectedNode->index();
emit tree->dataChanged(nodeIndex, nodeIndex);
}
}
QVariant EkamTreeNode::data(int role) {
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
// string title
return name;
case Qt::ToolTipRole:
// string tooltip
if (action == 0) {
return QVariant();
} else {
action->getPath();
}
case Qt::DecorationRole:
// icon
if (isDirectory) {
switch (state) {
case ekam::proto::TaskUpdate::State::DELETED:
return QIcon(QLatin1String(":/ekamdashboard/images/dir-deleted.png"));
case ekam::proto::TaskUpdate::State::PENDING:
return QIcon(QLatin1String(":/ekamdashboard/images/dir-pending.png"));
case ekam::proto::TaskUpdate::State::RUNNING:
return QIcon(QLatin1String(":/ekamdashboard/images/dir-running.png"));
case ekam::proto::TaskUpdate::State::DONE:
return QIcon(QLatin1String(":/ekamdashboard/images/dir-done.png"));
case ekam::proto::TaskUpdate::State::PASSED:
return QIcon(QLatin1String(":/ekamdashboard/images/dir-passed.png"));
case ekam::proto::TaskUpdate::State::FAILED:
return QIcon(QLatin1String(":/ekamdashboard/images/dir-failed.png"));
case ekam::proto::TaskUpdate::State::BLOCKED:
// Use pending icon for blocked.
return QIcon(QLatin1String(":/ekamdashboard/images/dir-pending.png"));
case DEFAULT_STATE:
return QIcon(QLatin1String(":/ekamdashboard/images/dir.png"));
}
} else {
switch (state) {
case DEFAULT_STATE:
case ekam::proto::TaskUpdate::State::DELETED:
return QIcon(QLatin1String(":/ekamdashboard/images/state-deleted.png"));
case ekam::proto::TaskUpdate::State::PENDING:
return QIcon(QLatin1String(":/ekamdashboard/images/state-pending.png"));
case ekam::proto::TaskUpdate::State::RUNNING:
return QIcon(QLatin1String(":/ekamdashboard/images/state-running.png"));
case ekam::proto::TaskUpdate::State::DONE:
return QIcon(QLatin1String(":/ekamdashboard/images/state-done.png"));
case ekam::proto::TaskUpdate::State::PASSED:
return QIcon(QLatin1String(":/ekamdashboard/images/state-passed.png"));
case ekam::proto::TaskUpdate::State::FAILED:
return QIcon(QLatin1String(":/ekamdashboard/images/state-failed.png"));
case ekam::proto::TaskUpdate::State::BLOCKED:
// Use pending icon for blocked.
return QIcon(QLatin1String(":/ekamdashboard/images/state-pending.png"));
}
}
qWarning() << "Can't get here.";
break;
case Qt::FontRole:
// We could bold this or something.
QFont result;
if (isDirectory) {
result.setBold(true);
}
return result;
}
return QVariant();
}
void EkamTreeNode::setAction(ActionState* newAction) {
if (action != 0) {
disconnect(action, SIGNAL(stateChanged(ekam::proto::TaskUpdate::State)),
this, SLOT(actionStateChanged(ekam::proto::TaskUpdate::State)));
disconnect(action, SIGNAL(removed()), this, SLOT(actionRemoved()));
}
action = newAction;
stateChanged(action == 0 ? DEFAULT_STATE : action->getState());
if (action != 0) {
connect(action, SIGNAL(stateChanged(ekam::proto::TaskUpdate::State)),
this, SLOT(actionStateChanged(ekam::proto::TaskUpdate::State)));
connect(action, SIGNAL(removed()), this, SLOT(actionRemoved()));
}
}
void EkamTreeNode::stateChanged(ekam::proto::TaskUpdate::State newState) {
if (state != newState) {
state = newState;
QModelIndex myIndex = index();
emit tree->dataChanged(myIndex, myIndex);
if (parentNode != 0) {
parentNode->childStateChanged();
}
}
}
void EkamTreeNode::childStateChanged() {
ekam::proto::TaskUpdate::State maxState = DEFAULT_STATE;
int maxStatePriority = STATE_PRIORITIES[static_cast<uint>(maxState)];
foreach (EkamTreeNode* child, childNodes) {
int childPriority = STATE_PRIORITIES[static_cast<uint>(child->state)];
if (childPriority > maxStatePriority) {
maxState = child->state;
maxStatePriority = childPriority;
}
}
stateChanged(maxState);
}
void EkamTreeNode::removeChild(EkamTreeNode* child) {
int r = child->row();
tree->beginRemoveRows(index(), r, r);
childNodes.erase(childNodes.begin() + r);
tree->endRemoveRows();
if (childNodes.empty() && parentNode != 0) {
parentNode->removeChild(this);
}
}
// =======================================================================================
EkamTreeModel::EkamTreeModel(EkamDashboardPlugin* plugin, QObject* parent)
: QAbstractItemModel(parent), plugin(plugin), root(new EkamTreeNode(this)) {
// qDebug() << "EkamTreeModel::EkamTreeModel(...)";
connect(plugin, SIGNAL(newAction(ActionState*)), this, SLOT(newAction(ActionState*)));
foreach (ActionState* action, plugin->allActions()) {
newAction(action);
}
}
EkamTreeModel::~EkamTreeModel() {}
QModelIndex EkamTreeModel::index(int row, int column, const QModelIndex & parent) const {
// qDebug() << "EkamTreeModel::index(" << row << ", " << column << ", " << parent << ")";
if (column != 0) {
// Invalid.
return QModelIndex();
}
EkamTreeNode* parentNode = indexToNode(parent);
if (row < 0 || row >= parentNode->childCount()) {
// Out of bounds.
return QModelIndex();
}
return createIndex(row, 0, parentNode->getChild(row));
}
QModelIndex EkamTreeModel::parent(const QModelIndex &index) const {
// qDebug() << "EkamTreeModel::parent(" << index << ")";
EkamTreeNode* node = indexToNode(index);
if (node == root) {
// This should never happen?
qWarning() << "Called parent() on invisible root object?";
return QModelIndex();
} else {
return reinterpret_cast<EkamTreeNode*>(node->parent())->index();
}
}
QVariant EkamTreeModel::data(const QModelIndex &index, int role) const {
// qDebug() << "EkamTreeModel::data(" << index << ", " << role << ") = "
// << indexToNode(index)->data(role);
return indexToNode(index)->data(role);
}
int EkamTreeModel::rowCount(const QModelIndex & parent) const {
// qDebug() << "EkamTreeModel::rowCount(" << parent << ") = "
// << indexToNode(parent)->childCount();
return indexToNode(parent)->childCount();
}
int EkamTreeModel::columnCount(const QModelIndex & parent) const {
// qDebug() << "EkamTreeModel::columnCount(" << parent << ") = 1";
Q_UNUSED(parent);
return 1;
}
bool EkamTreeModel::hasChildren(const QModelIndex & parent) const {
// qDebug() << "EkamTreeModel::hasChildren(" << parent << ") = "
// << (indexToNode(parent)->childCount() > 0);
return indexToNode(parent)->childCount() > 0;
}
void EkamTreeModel::newAction(ActionState* action) {
// qDebug() << "EkamTreeModel::newAction(" << action->getVerb() << ":" << action->getNoun() << ")";
root->createNode(action->getNoun(), action->getVerb(), action);
}
// =======================================================================================
EkamTreeWidget::EkamTreeWidget(EkamDashboardPlugin* plugin)
: QWidget(), plugin(plugin) {
model = new EkamTreeModel(plugin, this);
view = new Utils::NavigationTreeView(this);
view->setModel(model);
setFocusProxy(view);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(view);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(jumpTo(QModelIndex)));
}
EkamTreeWidget::~EkamTreeWidget() {}
void EkamTreeWidget::jumpTo(const QModelIndex& index) {
ActionState* action = model->indexToNode(index)->getAction();
if (action != 0) {
const ProjectExplorer::Task* task = action->firstTask();
Core::EditorManager* editorManager = Core::EditorManager::instance();
if (task == 0) {
// Open in editor.
editorManager->openEditor(action->getPath());
} else {
// Open first task in editor and issues list.
plugin->taskHub()->taskMarkClicked(task->taskId);
QString name = task->file.toString();
if (name.isEmpty()) {
name = action->getPath();
}
editorManager->openEditorAt(name, task->line);
}
}
}
// =======================================================================================
EkamTreeWidgetFactory::EkamTreeWidgetFactory(EkamDashboardPlugin* plugin)
: INavigationWidgetFactory(), plugin(plugin) {
setId("EkamActions");
setDisplayName(QLatin1String("Ekam Actions"));
setPriority(100);
}
EkamTreeWidgetFactory::~EkamTreeWidgetFactory() {}
Core::NavigationView EkamTreeWidgetFactory::createWidget() {
Core::NavigationView result;
EkamTreeWidget* tree = new EkamTreeWidget(plugin);
result.widget = tree;
return result;
}
} // namespace Internal
} // namespace EkamDashboard
================================================
FILE: qtcreator/ekamtreewidget.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EKAMTREEWIDGET_H
#define EKAMTREEWIDGET_H
#include <coreplugin/inavigationwidgetfactory.h>
#include <QWidget>
#include <QAbstractItemModel>
#include <QTreeView>
#include <projectexplorer/task.h>
#include "dashboard.capnp.h"
namespace EkamDashboard {
namespace Internal {
class EkamDashboardPlugin;
class EkamTreeModel;
class ActionState;
class EkamTreeNode : public QObject {
Q_OBJECT
public:
explicit EkamTreeNode(EkamTreeModel* tree);
EkamTreeNode(EkamTreeNode* parent, const QString& name, bool isDirectory);
virtual ~EkamTreeNode();
int row();
QModelIndex index();
void createNode(const QString& noun, const QString& verb, ActionState* action);
QVariant data(int role);
int childCount() {
return childNodes.size();
}
EkamTreeNode* getChild(int index) {
return childNodes.at(index);
}
ActionState* getAction() {
return action;
}
private slots:
void actionStateChanged(ekam::proto::TaskUpdate::State newState);
void actionRemoved();
private:
EkamTreeModel* tree;
bool isDirectory;
QString name;
EkamTreeNode* parentNode;
QList<EkamTreeNode*> childNodes;
ActionState* action;
ekam::proto::TaskUpdate::State state;
void setAction(ActionState* newAction);
void stateChanged(ekam::proto::TaskUpdate::State newState);
void childStateChanged();
void removeChild(EkamTreeNode* child);
};
class EkamTreeModel : public QAbstractItemModel {
Q_OBJECT
public:
EkamTreeModel(EkamDashboardPlugin* plugin, QObject* parent = 0);
virtual ~EkamTreeModel();
virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
virtual QModelIndex parent(const QModelIndex &index) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;
virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const;
EkamTreeNode* indexToNode(const QModelIndex& index) const {
return index.isValid() ? reinterpret_cast<EkamTreeNode*>(index.internalPointer()) : root;
}
private slots:
void newAction(ActionState* action);
private:
friend class EkamTreeNode;
EkamDashboardPlugin* plugin;
EkamTreeNode* root;
};
class EkamTreeWidget : public QWidget {
Q_OBJECT
public:
explicit EkamTreeWidget(EkamDashboardPlugin *plugin = 0);
virtual ~EkamTreeWidget();
private slots:
void jumpTo(const QModelIndex& index);
private:
EkamDashboardPlugin* plugin;
EkamTreeModel* model;
QTreeView* view;
};
class EkamTreeWidgetFactory : public Core::INavigationWidgetFactory {
Q_OBJECT
public:
explicit EkamTreeWidgetFactory(EkamDashboardPlugin* plugin);
virtual ~EkamTreeWidgetFactory();
virtual Core::NavigationView createWidget();
private:
EkamDashboardPlugin* plugin;
};
} // namespace Internal
} // namespace EkamDashboard
#endif // EKAMTREEWIDGET_H
================================================
FILE: src/base/Debug.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Debug.h"
#include <stdio.h>
namespace ekam {
DebugMessage::Severity DebugMessage::logLevel = WARNING;
int DebugMessage::counter = 0;
static const char* SEVERITY_NAMES[] = {
"INFO", "WARNING", "ERROR"
};
DebugMessage::DebugMessage(Severity severity, const char* filename, int line) {
*this << "ekam debug: " << SEVERITY_NAMES[severity] << ": "
<< filename << ":" << line << ": ";
}
DebugMessage::~DebugMessage() {
// TODO: We really need to buffer the message and write it all at once to avoid interleaved
// text when multiprocessing.
fputs("\n", stderr);
fflush(stderr);
++counter;
}
DebugMessage& DebugMessage::operator<<(const char* value) {
fputs(value, stderr);
return *this;
}
DebugMessage& DebugMessage::operator<<(const std::string& value) {
fwrite(value.data(), sizeof(char), value.size(), stderr);
return *this;
}
#define HANDLE_TYPE(TYPE, FORMAT) \
DebugMessage& DebugMessage::operator<<(TYPE value) { \
fprintf(stderr, FORMAT, value); \
return *this; \
}
HANDLE_TYPE(char, "%c");
HANDLE_TYPE(signed char, "%hhd");
HANDLE_TYPE(unsigned char, "%hhu");
HANDLE_TYPE(short, "%hd");
HANDLE_TYPE(unsigned short, "%hu");
HANDLE_TYPE(int, "%d");
HANDLE_TYPE(unsigned int, "%u");
HANDLE_TYPE(long, "%ld");
HANDLE_TYPE(unsigned long, "%lu");
HANDLE_TYPE(long long, "%lld");
HANDLE_TYPE(unsigned long long, "%llu");
HANDLE_TYPE(float, "%g");
HANDLE_TYPE(double, "%g");
HANDLE_TYPE(const void*, "%p");
} // namespace ekam
================================================
FILE: src/base/Debug.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef KENTONSCODE_BASE_DEBUGLOG_H_
#define KENTONSCODE_BASE_DEBUGLOG_H_
#include <string>
namespace ekam {
class DebugMessage {
public:
enum Severity {
INFO,
WARNING,
ERROR
};
DebugMessage(Severity severity, const char* filename, int line);
~DebugMessage();
DebugMessage& operator<<(const char* value);
DebugMessage& operator<<(const std::string& value);
DebugMessage& operator<<(char value);
DebugMessage& operator<<(signed char value);
DebugMessage& operator<<(unsigned char value);
DebugMessage& operator<<(short value);
DebugMessage& operator<<(unsigned short value);
DebugMessage& operator<<(int value);
DebugMessage& operator<<(unsigned int value);
DebugMessage& operator<<(long value);
DebugMessage& operator<<(unsigned long value);
DebugMessage& operator<<(long long value);
DebugMessage& operator<<(unsigned long long value);
DebugMessage& operator<<(float value);
DebugMessage& operator<<(double value);
DebugMessage& operator<<(const void* ptr);
inline static bool shouldLog(Severity severity, const char*, int) {
return severity >= logLevel;
}
inline static void setLogLevel(Severity severity) {
logLevel = severity;
}
// Useful for detecting if any log messages have been printed, e.g. to avoid clobbering them
// with terminal manipulations.
inline static int getMessageCount() { return counter; }
private:
static Severity logLevel;
static int counter;
};
#define DEBUG_LOG(SEVERITY) \
if (!::ekam::DebugMessage::shouldLog(::ekam::DebugMessage::SEVERITY, __FILE__, __LINE__)) {} \
else ::ekam::DebugMessage(::ekam::DebugMessage::SEVERITY, __FILE__, __LINE__)
#define DEBUG_INFO DEBUG_LOG(INFO)
#define DEBUG_WARNING DEBUG_LOG(WARNING)
#define DEBUG_ERROR DEBUG_LOG(ERROR)
} // namespace ekam
#endif // KENTONSCODE_BASE_DEBUGLOG_H_
================================================
FILE: src/base/Hash.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Hash.h"
#include <stdexcept>
#include "sha256.h"
namespace ekam {
namespace {
char HexDigit(unsigned int value) {
value &= 0x0F;
if (value < 10) {
return '0' + value;
} else {
return 'a' + value - 10;
}
}
} // anonymous namespace
Hash Hash::of(const std::string& data) {
return Builder().add(data).build();
}
Hash Hash::of(void* data, size_t size) {
return Builder().add(data, size).build();
}
// Note: Since this is in static space it will be automatically initialized to zero.
const Hash Hash::NULL_HASH;
std::string Hash::toString() const {
std::string result;
result.reserve(sizeof(hash) * 2);
for (unsigned int i = 0; i < sizeof(hash); i++) {
result.push_back(HexDigit(hash[i] >> 4));
result.push_back(HexDigit(hash[i]));
}
return result;
}
Hash::Builder::Builder() {
SHA256_Init(&context);
}
Hash::Builder& Hash::Builder::add(const std::string& data) {
SHA256_Update(&context, data.data(), data.size());
return *this;
}
Hash::Builder& Hash::Builder::add(void* data, size_t size) {
SHA256_Update(&context, data, size);
return *this;
}
Hash Hash::Builder::build() {
Hash result;
SHA256_Final(result.hash, &context);
return result;
}
} // namespace ekam
================================================
FILE: src/base/Hash.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef KENTONSCODE_BASE_HASH_H_
#define KENTONSCODE_BASE_HASH_H_
#include <inttypes.h>
#include <string.h>
#include <string>
#include "sha256.h"
namespace ekam {
class Hash {
public:
inline Hash() {}
class Builder {
public:
Builder();
Builder& add(const std::string& data);
Builder& add(void* data, size_t size);
Hash build();
private:
SHA256Context context;
};
static Hash of(const std::string& data);
static Hash of(void* data, size_t size);
static const Hash NULL_HASH;
std::string toString() const;
inline bool operator==(const Hash& other) const {
return memcmp(hash, other.hash, sizeof(hash)) == 0;
}
inline bool operator!=(const Hash& other) const {
return memcmp(hash, other.hash, sizeof(hash)) != 0;
}
inline bool operator<(const Hash& other) const {
return memcmp(hash, other.hash, sizeof(hash)) < 0;
}
inline bool operator>(const Hash& other) const {
return memcmp(hash, other.hash, sizeof(hash)) > 0;
}
inline bool operator<=(const Hash& other) const {
return memcmp(hash, other.hash, sizeof(hash)) <= 0;
}
inline bool operator>=(const Hash& other) const {
return memcmp(hash, other.hash, sizeof(hash)) >= 0;
}
class StlHashFunc {
public:
inline size_t operator()(const Hash& h) const {
return h.shortHash;
}
};
private:
union {
unsigned char hash[32];
size_t shortHash;
};
};
} // namespace ekam
#endif // KENTONSCODE_BASE_HASH_H_
================================================
FILE: src/base/OwnedPtr.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "OwnedPtr.h"
namespace ekam {
} // namespace ekam
================================================
FILE: src/base/OwnedPtr.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef KENTONSCODE_BASE_OWNEDPTR_H_
#define KENTONSCODE_BASE_OWNEDPTR_H_
#include <stddef.h>
#include <type_traits>
#include <vector>
#include <deque>
#include <queue>
#include <unordered_map>
#include <assert.h>
#ifdef __CDT_PARSER__
#define noexcept
#define constexpr
namespace std { struct nullptr_t; }
#endif
namespace ekam {
template <typename T>
inline void deleteEnsuringCompleteType(T* ptr) {
enum { type_must_be_complete = sizeof(T) };
delete ptr;
}
template <typename T>
class OwnedPtr {
public:
OwnedPtr() : ptr(NULL) {}
OwnedPtr(const OwnedPtr&) = delete;
OwnedPtr(OwnedPtr&& other) : ptr(other.releaseRaw()) {}
template <typename U>
OwnedPtr(OwnedPtr<U>&& other) : ptr(other.releaseRaw()) {}
OwnedPtr(std::nullptr_t) : ptr(NULL) {}
~OwnedPtr() {
deleteEnsuringCompleteType(ptr);
}
OwnedPtr& operator=(const OwnedPtr&) = delete;
OwnedPtr& operator=(OwnedPtr&& other) {
reset(other.releaseRaw());
return *this;
}
template <typename U>
OwnedPtr& operator=(OwnedPtr<U>&& other) {
reset(other.releaseRaw());
return *this;
}
T* get() const { return ptr; }
T* operator->() const { assert(ptr != NULL); return ptr; }
T& operator*() const { assert(ptr != NULL); return *ptr; }
OwnedPtr release() {
return OwnedPtr(releaseRaw());
}
void clear() {
reset(NULL);
}
bool operator==(const T* other) { return ptr == other; }
bool operator!=(const T* other) { return ptr != other; }
private:
T* ptr;
explicit OwnedPtr(T* ptr) : ptr(ptr) {}
void reset(T* newValue) {
T* oldValue = ptr;
ptr = newValue;
deleteEnsuringCompleteType(oldValue);
}
T* releaseRaw() {
T* result = ptr;
ptr = NULL;
return result;
}
template <typename U>
friend class OwnedPtr;
template <typename U, typename... Params>
friend OwnedPtr<U> newOwned(Params&&... params);
template <typename U>
friend class SmartPtr;
template <typename U>
friend class OwnedPtrVector;
template <typename U>
friend class OwnedPtrDeque;
template <typename U>
friend class OwnedPtrQueue;
template <typename Key, typename U, typename HashFunc, typename EqualsFunc>
friend class OwnedPtrMap;
};
template <typename T, typename... Params>
OwnedPtr<T> newOwned(Params&&... params) {
return OwnedPtr<T>(new T(std::forward<Params>(params)...));
}
template <typename T>
class Indirect {
public:
template <typename... Params>
Indirect(Params&&... params): ptr(newOwned<T>(std::forward<Params>(params)...)) {}
Indirect(Indirect&& other): ptr(other.ptr.release()) {}
Indirect(const Indirect& other): ptr(newOwned<T>(*other.ptr)) {}
Indirect& operator=(Indirect&& other) { ptr = other.ptr.release(); return *this; }
Indirect& operator=(const Indirect& other) { ptr = newOwned<T>(*other.ptr); return *this; }
bool operator==(const Indirect& other) const { return *ptr == *other.ptr; }
bool operator!=(const Indirect& other) const { return *ptr != *other.ptr; }
const T& operator*() const { return *ptr; }
T& operator*() { return *ptr; }
const T* operator->() const { return ptr.get(); }
T* operator->() { return ptr.get(); }
private:
OwnedPtr<T> ptr;
};
// TODO: Hide this somewhere private?
class Refcount {
public:
Refcount(): strong(1), weak(0) {}
Refcount(const Refcount& other) = delete;
Refcount& operator=(const Refcount& other) = delete;
static void inc(Refcount* r) {
if (r != NULL) ++r->strong;
}
static bool dec(Refcount* r) {
if (r == NULL) {
return false;
} else if (--r->strong == 0) {
if (r->weak == 0) {
delete r;
}
return true;
} else {
return false;
}
}
static void incWeak(Refcount* r) {
if (r != NULL) ++r->weak;
}
static void decWeak(Refcount* r) {
if (r != NULL && --r->weak == 0 && r->strong == 0) {
delete r;
}
}
static bool release(Refcount* r) {
if (r != NULL && r->strong == 1) {
dec(r);
return true;
} else {
return false;
}
}
static bool isLive(Refcount* r) {
return r != NULL && r->strong > 0;
}
private:
int strong;
int weak;
};
template <typename T>
class SmartPtr {
public:
SmartPtr() : ptr(NULL), refcount(NULL) {}
SmartPtr(std::nullptr_t) : ptr(NULL), refcount(NULL) {}
~SmartPtr() {
if (Refcount::dec(refcount)) {
deleteEnsuringCompleteType(ptr);
}
}
SmartPtr(const SmartPtr& other) : ptr(other.ptr), refcount(other.refcount) {
Refcount::inc(refcount);
}
template <typename U>
SmartPtr(const SmartPtr<U>& other) : ptr(other.ptr), refcount(other.refcount) {
Refcount::inc(refcount);
}
SmartPtr& operator=(const SmartPtr& other) {
reset(other.ptr, other.refcount);
return *this;
}
template <typename U>
SmartPtr& operator=(const SmartPtr<U>& other) {
reset(other.ptr, other.refcount);
return *this;
}
SmartPtr(SmartPtr&& other) : ptr(other.ptr), refcount(other.refcount) {
other.ptr = NULL;
other.refcount = NULL;
}
template <typename U>
SmartPtr(SmartPtr<U>&& other) : ptr(other.ptr), refcount(other.refcount) {
other.ptr = NULL;
other.refcount = NULL;
}
SmartPtr& operator=(SmartPtr&& other) {
// Move pointers to locals before reset() in case &other == this.
T* tempPtr = other.ptr;
Refcount* tempRefcount = other.refcount;
other.ptr = NULL;
other.refcount = NULL;
reset(NULL, NULL);
ptr = tempPtr;
refcount = tempRefcount;
return *this;
}
template <typename U>
SmartPtr& operator=(SmartPtr<U>&& other) {
// Move pointers to locals before reset() in case &other == this.
T* tempPtr = other.ptr;
Refcount* tempRefcount = other.refcount;
other.ptr = NULL;
other.refcount = NULL;
reset(NULL, NULL);
ptr = tempPtr;
refcount = tempRefcount;
return *this;
}
template <typename U>
SmartPtr(OwnedPtr<U>&& other)
: ptr(other.releaseRaw()),
refcount(ptr == NULL ? NULL : new Refcount()) {}
template <typename U>
SmartPtr& operator=(OwnedPtr<U>&& other) {
reset(other.releaseRaw());
return *this;
}
T* get() const { return ptr; }
T* operator->() const { assert(ptr != NULL); return ptr; }
T& operator*() const { assert(ptr != NULL); return *ptr; }
template <typename U>
bool release(OwnedPtr<U>* other) {
if (Refcount::release(refcount)) {
other->reset(ptr);
ptr = NULL;
return true;
} else {
return false;
}
}
void clear() {
reset(NULL);
}
bool operator==(const T* other) { return ptr == other; }
bool operator!=(const T* other) { return ptr != other; }
bool operator==(const SmartPtr<T>& other) { return ptr == other.ptr; }
bool operator!=(const SmartPtr<T>& other) { return ptr != other.ptr; }
void allocate() {
reset(new T());
}
template <typename P1>
void allocate(const P1& p1) {
reset(new T(p1));
}
template <typename P1, typename P2>
void allocate(const P1& p1, const P2& p2) {
reset(new T(p1, p2));
}
template <typename P1, typename P2, typename P3>
void allocate(const P1& p1, const P2& p2, const P3& p3) {
reset(new T(p1, p2, p3));
}
template <typename P1, typename P2, typename P3, typename P4>
void allocate(const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
reset(new T(p1, p2, p3, p4));
}
template <typename Sub>
void allocateSubclass() {
reset(new Sub());
}
template <typename Sub, typename P1>
void allocateSubclass(const P1& p1) {
reset(new Sub(p1));
}
template <typename Sub, typename P1, typename P2>
void allocateSubclass(const P1& p1, const P2& p2) {
reset(new Sub(p1, p2));
}
template <typename Sub, typename P1, typename P2, typename P3>
void allocateSubclass(const P1& p1, const P2& p2, const P3& p3) {
reset(new Sub(p1, p2, p3));
}
template <typename Sub, typename P1, typename P2, typename P3, typename P4>
void allocateSubclass(const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
reset(new Sub(p1, p2, p3, p4));
}
private:
T* ptr;
Refcount* refcount;
inline void reset(T* newValue) {
reset(newValue, newValue == NULL ? NULL : new Refcount());
Refcount::dec(refcount);
}
void reset(T* newValue, Refcount* newRefcount) {
T* oldValue = ptr;
Refcount* oldRefcount = refcount;
ptr = newValue;
refcount = newRefcount;
Refcount::inc(refcount);
if (Refcount::dec(oldRefcount)) {
deleteEnsuringCompleteType(oldValue);
}
}
template <typename U>
friend class SmartPtr;
template <typename U>
friend class WeakPtr;
template <typename U>
friend class OwnedPtr;
template <typename U>
friend class OwnedPtrVector;
template <typename U>
friend class OwnedPtrQueue;
template <typename Key, typename U, typename HashFunc, typename EqualsFunc>
friend class OwnedPtrMap;
};
template <typename T>
class WeakPtr {
public:
WeakPtr(): ptr(NULL), refcount(NULL) {}
WeakPtr(const WeakPtr& other): ptr(other.ptr), refcount(other.refcount) {
Refcount::incWeak(refcount);
}
WeakPtr(const SmartPtr<T>& other): ptr(other.ptr), refcount(other.refcount) {
Refcount::incWeak(refcount);
}
WeakPtr(std::nullptr_t): ptr(nullptr), refcount(nullptr) {}
~WeakPtr() {
Refcount::decWeak(refcount);
}
WeakPtr& operator=(const WeakPtr& other) {
Refcount* oldRefcount = refcount;
ptr = other.ptr;
refcount = other.refcount;
Refcount::incWeak(refcount);
Refcount::decWeak(oldRefcount);
return *this;
}
template <typename U>
WeakPtr& operator=(const WeakPtr<U>& other) {
Refcount* oldRefcount = refcount;
ptr = other.ptr;
refcount = other.refcount;
Refcount::incWeak(refcount);
Refcount::decWeak(oldRefcount);
return *this;
}
template <typename U>
WeakPtr& operator=(const SmartPtr<U>& other) {
Refcount* oldRefcount = refcount;
ptr = other.ptr;
refcount = other.refcount;
Refcount::incWeak(refcount);
Refcount::decWeak(oldRefcount);
return *this;
}
WeakPtr& operator=(std::nullptr_t) {
Refcount::decWeak(refcount);
ptr = nullptr;
refcount = nullptr;
return *this;
}
template <typename U>
operator SmartPtr<U>() const {
SmartPtr<U> result;
if (Refcount::isLive(refcount)) {
result.reset(ptr, refcount);
}
return result;
}
private:
T* ptr;
Refcount* refcount;
};
template <typename T>
class OwnedPtrVector {
public:
OwnedPtrVector() {}
OwnedPtrVector(const OwnedPtrVector&) = delete;
OwnedPtrVector(OwnedPtrVector&& other) {
vec.swap(other.vec);
}
~OwnedPtrVector() {
for (typename std::vector<T*>::const_iterator iter = vec.begin(); iter != vec.end(); ++iter) {
deleteEnsuringCompleteType(*iter);
}
}
OwnedPtrVector& operator=(const OwnedPtrVector&) = delete;
int size() const { return vec.size(); }
T* get(int index) const { return vec[index]; }
bool empty() const { return vec.empty(); }
void add(OwnedPtr<T> ptr) {
vec.push_back(ptr.releaseRaw());
}
void set(int index, OwnedPtr<T> ptr) {
deleteEnsuringCompleteType(vec[index]);
vec[index] = ptr->releaseRaw();
}
OwnedPtr<T> release(int index) {
T* result = vec[index];
vec[index] = NULL;
return OwnedPtr<T>(result);
}
OwnedPtr<T> releaseBack() {
T* result = vec.back();
vec.pop_back();
return OwnedPtr<T>(result);
}
OwnedPtr<T> releaseAndShift(int index) {
T* result = vec[index];
vec.erase(vec.begin() + index);
return OwnedPtr<T>(result);
}
void clear() {
for (typename std::vector<T*>::const_iterator iter = vec.begin(); iter != vec.end(); ++iter) {
deleteEnsuringCompleteType(*iter);
}
vec.clear();
}
void swap(OwnedPtrVector* other) {
vec.swap(other->vec);
}
class Appender {
public:
explicit Appender(OwnedPtrVector* vec) : vec(vec) {}
void add(OwnedPtr<T> ptr) {
vec->add(ptr.release());
}
private:
OwnedPtrVector* vec;
};
Appender appender() {
return Appender(this);
}
private:
std::vector<T*> vec;
};
template <typename T>
class OwnedPtrDeque {
public:
OwnedPtrDeque() {}
~OwnedPtrDeque() {
for (typename std::deque<T*>::const_iterator iter = q.begin(); iter != q.end(); ++iter) {
deleteEnsuringCompleteType(*iter);
}
}
int size() const { return q.size(); }
T* get(int index) const { return q[index]; }
bool empty() const { return q.empty(); }
void pushFront(OwnedPtr<T> ptr) {
q.push_front(ptr.releaseRaw());
}
OwnedPtr<T> popFront() {
T* ptr = q.front();
q.pop_front();
return OwnedPtr<T>(ptr);
}
void pushBack(OwnedPtr<T> ptr) {
q.push_back(ptr.releaseRaw());
}
OwnedPtr<T> popBack() {
T* ptr = q.back();
q.pop_back();
return OwnedPtr<T>(ptr);
}
OwnedPtr<T> releaseAndShift(int index) {
T* ptr = q[index];
q.erase(q.begin() + index);
return OwnedPtr<T>(ptr);
}
void clear() {
for (typename std::deque<T*>::const_iterator iter = q.begin(); iter != q.end(); ++iter) {
deleteEnsuringCompleteType(*iter);
}
q.clear();
}
void swap(OwnedPtrDeque* other) {
q.swap(other->q);
}
private:
std::deque<T*> q;
};
template <typename T>
class OwnedPtrQueue {
public:
OwnedPtrQueue() {}
~OwnedPtrQueue() {
clear();
}
int size() const { return q.size(); }
bool empty() const { return q.empty(); }
void push(OwnedPtr<T> ptr) {
q.push(ptr.releaseRaw());
}
OwnedPtr<T> pop() {
T* ptr = q.front();
q.pop();
return OwnedPtr<T>(ptr);
}
void clear() {
while (!q.empty()) {
deleteEnsuringCompleteType(q.front());
q.pop();
}
}
class Appender {
public:
Appender(OwnedPtrQueue* q) : q(q) {}
void add(OwnedPtr<T> ptr) {
q->push(ptr);
}
private:
OwnedPtrQueue* q;
};
Appender appender() {
return Appender(this);
}
private:
std::queue<T*> q;
};
template <typename Key, typename T,
typename HashFunc = std::hash<Key>,
typename EqualsFunc = std::equal_to<Key> >
class OwnedPtrMap {
typedef std::unordered_map<Key, T*, HashFunc, EqualsFunc> InnerMap;
public:
OwnedPtrMap() {}
~OwnedPtrMap() {
for (typename InnerMap::const_iterator iter = map.begin();
iter != map.end(); ++iter) {
deleteEnsuringCompleteType(iter->second);
}
}
bool empty() const {
return map.empty();
}
int size() const {
return map.size();
}
bool contains(const Key& key) const {
return map.count(key) > 0;
}
T* get(const Key& key) const {
typename InnerMap::const_iterator iter = map.find(key);
if (iter == map.end()) {
return NULL;
} else {
return iter->second;
}
}
void add(const Key& key, OwnedPtr<T> ptr) {
T* value = ptr.releaseRaw();
std::pair<typename InnerMap::iterator, bool> insertResult =
map.insert(std::make_pair(key, value));
if (!insertResult.second) {
deleteEnsuringCompleteType(insertResult.first->second);
insertResult.first->second = value;
}
}
bool addIfNew(const Key& key, OwnedPtr<T> ptr) {
T* value = ptr.releaseRaw();
std::pair<typename InnerMap::iterator, bool> insertResult =
map.insert(std::make_pair(key, value));
if (insertResult.second) {
return true;
} else {
deleteEnsuringCompleteType(value);
return false;
}
}
bool release(const Key& key, OwnedPtr<T>* output) {
typename InnerMap::iterator iter = map.find(key);
if (iter == map.end()) {
output->reset(NULL);
return false;
} else {
output->reset(iter->second);
map.erase(iter);
return true;
}
}
void releaseAll(typename OwnedPtrVector<T>::Appender output) {
for (typename InnerMap::const_iterator iter = map.begin();
iter != map.end(); ++iter) {
output.add(OwnedPtr<T>(iter->second));
}
map.clear();
}
bool erase(const Key& key) {
typename InnerMap::iterator iter = map.find(key);
if (iter == map.end()) {
return false;
} else {
deleteEnsuringCompleteType(iter->second);
map.erase(iter);
return true;
}
}
void clear() {
for (typename InnerMap::const_iterator iter = map.begin();
iter != map.end(); ++iter) {
deleteEnsuringCompleteType(iter->second);
}
map.clear();
}
void swap(OwnedPtrMap* other) {
map.swap(other->map);
}
class Iterator {
public:
Iterator(const OwnedPtrMap& map)
: nextIter(map.map.begin()),
end(map.map.end()) {}
bool next() {
if (nextIter == end) {
return false;
} else {
iter = nextIter;
++nextIter;
return true;
}
}
const Key& key() {
return iter->first;
}
T* value() {
return iter->second;
}
private:
typename InnerMap::const_iterator iter;
typename InnerMap::const_iterator nextIter;
typename InnerMap::const_iterator end;
};
private:
InnerMap map;
};
} // namespace ekam
#endif // KENTONSCODE_BASE_OWNEDPTR_H_
================================================
FILE: src/base/Promise.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Promise.h"
namespace ekam {
Runnable::~Runnable() {}
PendingRunnable::~PendingRunnable() {}
Executor::~Executor() noexcept(false) {}
} // namespace ekam
================================================
FILE: src/base/Promise.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef KENTONSCODE_BASE_PROMISE_H_
#define KENTONSCODE_BASE_PROMISE_H_
#include <set>
#include <vector>
#include <functional>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
#include "OwnedPtr.h"
#include "Debug.h"
namespace ekam {
// =======================================================================================
// TODO: Put elsewhere
enum class Void {
VOID
};
template <typename... Types>
class ValuePack;
template <typename Head, typename... Tail>
class ValuePack<Head, Tail...>: private ValuePack<Tail...> {
public:
ValuePack(Head&& head, Tail&&... tail)
: ValuePack<Tail...>(std::forward<Tail>(tail)...),
head(std::forward<Head>(head)) {}
ValuePack(const ValuePack& other) = delete;
ValuePack(ValuePack&& other) = default;
ValuePack& operator=(const ValuePack& other) = delete;
ValuePack& operator=(ValuePack&& other) {
head = std::move(other.head);
ValuePack<Tail...>::operator=(std::move(other));
}
template <typename Func, typename... Params>
auto apply(const Func& func, Params&&... params) const ->
decltype(func(std::declval<Params>()..., std::declval<const Head&>(),
std::declval<const Tail&>()...)) {
return ValuePack<Tail...>::apply(func, std::forward<Params>(params)..., head);
}
template <typename Func, typename... Params>
auto applyMoving(const Func& func, Params&&... params) ->
decltype(func(std::declval<Params>()..., std::declval<Head>(), std::declval<Tail>()...)) {
return ValuePack<Tail...>::applyMoving(func, std::forward<Params>(params)..., std::move(head));
}
private:
Head head;
};
template <>
class ValuePack<> {
public:
ValuePack() {}
template <typename Func, typename... Params>
auto apply(const Func& func, Params&&... params) const ->
decltype(func(std::declval<Params>()...)) {
func(std::forward<Params>(params)...);
}
template <typename Func, typename... Params>
auto applyMoving(const Func& func, Params&&... params) ->
decltype(func(std::declval<Params>()...)) {
func(std::forward<Params>(params)...);
}
};
// =======================================================================================
// TODO: Put elsewhere
class WeakLink {
public:
WeakLink(): other(nullptr) {}
WeakLink(WeakLink* other): other(other) {
if (other != nullptr) {
other->disentangle();
other->other = this;
}
}
~WeakLink() {
disentangle();
}
void entangle(WeakLink* other) {
disentangle();
this->other = other;
if (other != nullptr) {
other->disentangle();
other->other = this;
}
}
bool isEntangled() {
return other != nullptr;
}
private:
WeakLink* other;
void disentangle() {
if (other != nullptr) {
other->other = nullptr;
other = nullptr;
}
}
};
// =======================================================================================
template <typename T>
class Promise;
class Runnable {
public:
virtual ~Runnable();
virtual void run() = 0;
};
class PendingRunnable {
public:
virtual ~PendingRunnable();
};
class Executor {
public:
virtual ~Executor() noexcept(false);
virtual OwnedPtr<PendingRunnable> runLater(OwnedPtr<Runnable> runnable) = 0;
template <typename... Types>
class When;
template <typename... Types>
When<typename std::remove_reference<Types>::type...> when(Types&&... params);
};
template <typename Func>
class LambdaRunnable: public Runnable {
public:
LambdaRunnable(Func&& func): func(std::move(func)) {}
~LambdaRunnable() {}
void run() {
func();
}
private:
Func func;
};
template <typename Func>
OwnedPtr<Runnable> newLambdaRunnable(Func&& func) {
return newOwned<LambdaRunnable<Func>>(std::move(func));
}
// =======================================================================================
template <typename T>
class MaybeException {
public:
MaybeException(): broken(false) {
new(&this->value) T;
}
MaybeException(T value): broken(false) {
new(&this->value) T(std::move(value));
}
MaybeException(std::exception_ptr error): broken(true) {
new(&this->error) std::exception_ptr(std::move(error));
}
MaybeException(const MaybeException& other) {
if (broken) {
new(&error) std::exception_ptr(other.error);
} else {
new(&value) T(other.value);
}
}
MaybeException(MaybeException&& other): broken(other.broken) {
if (broken) {
new(&error) std::exception_ptr(std::move(other.error));
} else {
new(&value) T(std::move(other.value));
}
}
~MaybeException() {
if (broken) {
error.~exception_ptr();
} else {
value.~T();
}
}
MaybeException& operator=(const MaybeException& other) {
this->~MaybeException();
broken = other.broken;
if (broken) {
new(&error) std::exception_ptr(other.error);
} else {
new(&value) T(other.value);
}
return *this;
}
MaybeException& operator=(MaybeException&& other) {
this->~MaybeException();
broken = other.broken;
if (broken) {
new(&error) std::exception_ptr(std::move(other.error));
} else {
new(&value) T(std::move(other.value));
}
return *this;
}
bool isException() {
return broken;
}
const T& get() {
if (broken) {
std::rethrow_exception(error);
}
return value;
}
T release() {
if (broken) {
std::rethrow_exception(error);
}
return std::move(value);
}
private:
bool broken;
union {
T value;
std::exception_ptr error;
};
};
template <>
class MaybeException<void> {
public:
MaybeException(): broken(false) {}
MaybeException(std::exception_ptr error): broken(true) {
new(&this->error) std::exception_ptr(std::move(error));
}
MaybeException(const MaybeException& other) {
if (broken) {
new(&error) std::exception_ptr(other.error);
}
}
MaybeException(MaybeException&& other): broken(other.broken) {
if (broken) {
new(&error) std::exception_ptr(std::move(other.error));
}
}
~MaybeException() {
if (broken) {
error.~exception_ptr();
}
}
MaybeException& operator=(const MaybeException& other) {
this->~MaybeException();
broken = other.broken;
if (broken) {
new(&error) std::exception_ptr(other.error);
}
return *this;
}
MaybeException& operator=(MaybeException&& other) {
this->~MaybeException();
broken = other.broken;
if (broken) {
new(&error) std::exception_ptr(std::move(other.error));
}
return *this;
}
bool isException() {
return broken;
}
Void get() {
if (broken) {
std::rethrow_exception(error);
}
return Void::VOID;
}
Void release() {
if (broken) {
std::rethrow_exception(error);
}
return Void::VOID;
}
private:
bool broken;
union {
std::exception_ptr error;
};
};
// =======================================================================================
template <typename T>
class PromiseFulfiller {
public:
typedef T PromiseType;
class Callback;
};
namespace promiseInternal {
struct PromiseConstructors;
template <typename T, typename Func, typename ExceptionHandler, typename ParamPack>
class DependentPromiseFulfiller;
class PromiseListener {
public:
virtual void dependencyDone(bool failed) = 0;
};
template <typename T>
class PromiseState {
public:
PromiseState(): owner(nullptr), listener(nullptr), fulfilled(false), failed(false) {}
virtual ~PromiseState() {}
Promise<T>* owner;
OwnedPtr<PromiseState> chainedPromise;
// (effectively) private:
PromiseListener* listener;
bool fulfilled;
bool failed;
MaybeException<T> value;
void setListener(PromiseListener* listener) {
if (this->listener != nullptr) {
throw std::invalid_argument("Already waiting on this Promise.");
}
this->listener = listener;
if (fulfilled) {
listener->dependencyDone(failed);
}
}
void preFulfill() {
if (fulfilled) {
throw std::logic_error("Already fulfilled this promise.");
}
fulfilled = true;
}
void postFulfill(bool failed) {
this->failed = failed;
if (listener != nullptr) {
listener->dependencyDone(failed);
}
}
void fulfill(const T& value) {
preFulfill();
this->value = value;
postFulfill(false);
}
void fulfill(T&& value) {
preFulfill();
this->value = std::move(value);
postFulfill(false);
}
void fulfill(Promise<T> chainedPromise);
void propagateCurrentException() {
preFulfill();
this->value = std::current_exception();
postFulfill(true);
}
T release() {
return value.release();
}
MaybeException<T> releaseMaybeException() {
return std::move(value);
}
template <typename U, typename Func, typename ExceptionHandler, typename ParamPack>
friend class DependentPromiseFulfiller;
friend struct PromiseConstructors;
};
template <>
class PromiseState<void> {
public:
PromiseState(): owner(nullptr), listener(nullptr), fulfilled(false), failed(false) {}
virtual ~PromiseState() {}
Promise<void>* owner;
OwnedPtr<PromiseState> chainedPromise;
private:
PromiseListener* listener;
bool fulfilled;
bool failed;
MaybeException<void> value;
void setListener(PromiseListener* listener) {
if (this->listener != nullptr) {
throw std::invalid_argument("Already waiting on this Promise.");
}
this->listener = listener;
if (fulfilled) {
listener->dependencyDone(failed);
}
}
void preFulfill() {
if (fulfilled) {
throw std::logic_error("Already fulfilled this promise.");
}
fulfilled = true;
}
void postFulfill(bool failed) {
this->failed = failed;
if (listener != nullptr) {
listener->dependencyDone(failed);
}
}
void fulfill() {
preFulfill();
postFulfill(false);
}
void fulfill(Promise<void> chainedPromise);
void propagateCurrentException() {
preFulfill();
this->value = std::current_exception();
postFulfill(true);
}
Void release() {
return value.release();
}
MaybeException<void> releaseMaybeException() {
return std::move(value);
}
friend class PromiseFulfiller<void>::Callback;
template <typename U, typename Func, typename ExceptionHandler, typename ParamPack>
friend class DependentPromiseFulfiller;
friend struct PromiseConstructors;
};
template <typename T>
struct Unpack {
typedef T Type;
};
template <typename T>
struct Unpack<Promise<T>> {
typedef T Type;
};
template <>
struct Unpack<Promise<void>> {
typedef Void Type;
};
template <typename T>
struct Chain {
typedef T Type;
};
template <typename T>
struct Chain<Promise<T>> {
typedef T Type;
};
// =======================================================================================
template <typename T, typename PromiseFulfillerImpl>
class FulfillerPromiseState;
} // namespace promiseInternal
template <typename T>
class PromiseFulfiller<T>::Callback {
public:
template <typename U>
void fulfill(U&& value) {
state->fulfill(std::forward<U>(value));
}
void propagateCurrentException() {
state->propagateCurrentException();
}
private:
promiseInternal::PromiseState<T>* state;
Callback(promiseInternal::PromiseState<T>* state): state(state) {}
~Callback() {}
template <typename U, typename PromiseFulfillerImpl>
friend class promiseInternal::FulfillerPromiseState;
};
template <>
class PromiseFulfiller<void>::Callback {
public:
void fulfill() {
state->fulfill();
}
void fulfill(Promise<void> chain);
void propagateCurrentException() {
state->propagateCurrentException();
}
private:
promiseInternal::PromiseState<void>* state;
Callback(promiseInternal::PromiseState<void>* state): state(state) {}
~Callback() {}
template <typename U, typename PromiseFulfillerImpl>
friend class promiseInternal::FulfillerPromiseState;
};
namespace promiseInternal {
template <typename T, typename PromiseFulfillerImpl>
class FulfillerPromiseState : public PromiseState<T> {
public:
template <typename... Params>
FulfillerPromiseState(Params&&... params)
: PromiseState<T>(),
callback(this),
fulfillerImpl(&callback, std::forward<Params>(params)...) {}
virtual ~FulfillerPromiseState() {}
private:
typename PromiseFulfiller<T>::Callback callback;
PromiseFulfillerImpl fulfillerImpl;
};
// =======================================================================================
template <typename ReturnType>
struct CallAndFulfillFunctor {
template <typename Callback, typename Func2, typename... Params>
void operator()(WeakLink* linkToFulfiller, Callback* callback,
Func2& func, Params&&... params) const {
try {
auto result = func(std::forward<Params>(params)...);
if (linkToFulfiller->isEntangled()) {
callback->fulfill(std::move(result));
}
} catch (...) {
callback->propagateCurrentException();
}
}
};
template <>
struct CallAndFulfillFunctor<void> {
template <typename Callback, typename Func2, typename... Params>
void operator()(WeakLink* linkToFulfiller, Callback* callback,
Func2& func, Params&&... params) const {
try {
func(std::forward<Params>(params)...);
if (linkToFulfiller->isEntangled()) {
callback->fulfill();
}
} catch (...) {
callback->propagateCurrentException();
}
}
};
template <typename T, typename Func, typename ExceptionHandler, typename ParamPack>
class DependentPromiseFulfiller : public PromiseFulfiller<T>, public PromiseListener {
public:
typedef typename PromiseFulfiller<T>::Callback Callback;
DependentPromiseFulfiller(Callback* callback, Executor* executor, Func&& func,
ExceptionHandler&& exceptionHandler, ParamPack&& params)
: callback(callback),
executor(executor),
func(std::move(func)),
exceptionHandler(std::move(exceptionHandler)),
params(std::move(params)),
dependencyFailed(false),
dependenciesLeft(1) {
addAllDependencies();
}
~DependentPromiseFulfiller() {
if (pendingReadyLater != nullptr) {
DEBUG_INFO << "Promise canceled: " << this;
}
}
void dependencyDone(bool failed) {
if (failed) {
dependencyFailed = true;
}
if (--dependenciesLeft == 0) {
readyLater();
}
}
Callback* callback;
Executor* executor;
Func func;
ExceptionHandler exceptionHandler;
ParamPack params;
private:
bool dependencyFailed;
int dependenciesLeft;
OwnedPtr<PendingRunnable> pendingReadyLater;
WeakLink weakLink;
struct AddDependenciesFunctor {
template <typename First, typename... Rest>
void operator()(DependentPromiseFulfiller* fulfiller, const Promise<First>& first,
Rest&&... rest) const {
++fulfiller->dependenciesLeft;
first.state->setListener(fulfiller);
operator()(fulfiller, std::forward<Rest>(rest)...);
}
template <typename First, typename... Rest>
void operator()(DependentPromiseFulfiller* fulfiller, First&& first, Rest&&... rest) const {
operator()(fulfiller, std::forward<Rest>(rest)...);
}
void operator()(DependentPromiseFulfiller* fulfiller) const {
if (--fulfiller->dependenciesLeft == 0) {
fulfiller->readyLater();
}
}
};
void addAllDependencies() {
params.apply(AddDependenciesFunctor(), this);
}
template <typename U>
static U&& unpack(U&& value) {
return std::forward<U>(value);
}
template <typename U>
static typename Unpack<Promise<U>>::Type unpack(Promise<U>&& promise) {
return promise.state->release();
}
struct DoReadyFunctor {
template <typename... Params>
void operator()(WeakLink* linkToFulfiller, Callback* callback,
Func& func, Params&&... params) const {
CallAndFulfillFunctor<decltype(func(unpack(std::forward<Params>(params))...))>()(
linkToFulfiller, callback, func, unpack(std::forward<Params>(params))...);
}
};
void ready() {
// Move stuff to stack in case promise is deleted during callback.
ParamPack localParams(std::move(params));
Func localFunc(std::move(func));
WeakLink linkToFulfiller(&weakLink);
localParams.applyMoving(DoReadyFunctor(), &linkToFulfiller, callback, localFunc);
}
template <typename U>
static U&& unpackMaybeException(U&& value) {
return std::forward<U>(value);
}
template <typename U>
static MaybeException<U> unpackMaybeException(Promise<U>&& promise) {
return promise.state->releaseMaybeException();
}
struct DoErrorFunctor {
template <typename... Params>
void operator()(WeakLink* linkToFulfiller, Callback* callback,
const ExceptionHandler& exceptionHandler, Params&&... params) const {
CallAndFulfillFunctor<decltype(
exceptionHandler(unpackMaybeException(std::forward<Params>(params))...))>()(
linkToFulfiller, callback, exceptionHandler,
unpackMaybeException(std::forward<Params>(params))...);
}
};
void error() {
// Move stuff to stack in case promise is deleted during callback.
ParamPack localParams(std::move(this->params));
ExceptionHandler localExceptionHandler(std::move(this->exceptionHandler));
WeakLink linkToFulfiller(&weakLink);
localParams.applyMoving(DoErrorFunctor(), &linkToFulfiller, callback, localExceptionHandler);
}
void readyLater() {
pendingReadyLater = executor->runLater(newLambdaRunnable(
[this]() {
auto objectToDeleteOnReturn = this->pendingReadyLater.release();
if (this->dependencyFailed) {
this->error();
} else {
this->ready();
}
}));
}
};
// =======================================================================================
template <typename ReturnType>
struct ForceErrorFunctor {
template <typename Head, typename... Tail>
ReturnType operator()(Head&& head, Tail&&... tail) const {
return operator()(tail...);
}
template <typename T, typename... Tail>
ReturnType operator()(MaybeException<T>&& head, Tail&&... tail) const {
head.get();
return operator()(tail...);
}
ReturnType operator()() const {
throw std::logic_error(
"Promise implementation failure: Exception handler called with no exceptions.");
}
};
} // namespace promiseInternal
// =======================================================================================
template <typename T>
class Promise {
public:
Promise() {}
Promise(const Promise& other) = delete;
Promise(Promise&& other): state(other.state.release()) {
if (state != nullptr) {
state->owner = this;
}
}
Promise& operator=(const Promise& other) = delete;
Promise& operator=(Promise&& other) {
state = other.state.release();
state->owner = this;
return *this;
}
bool operator==(std::nullptr_t) {
return state == nullptr;
}
bool operator!=(std::nullptr_t) {
return state != nullptr;
}
T* operator->() const {
return state->getValue()->operator->();
}
Promise<T> release() {
return std::move(*this);
}
private:
typedef promiseInternal::PromiseState<T> State;
explicit Promise(OwnedPtr<State> state): state(state.release()) {
while (this->state->chainedPromise != nullptr) {
this->state = this->state->chainedPromise.release();
}
this->state->owner = this;
}
OwnedPtr<State> state;
friend class Executor;
friend struct promiseInternal::PromiseConstructors;
template <typename U, typename Func, typename ExceptionHandler, typename ParamPack>
friend class promiseInternal::DependentPromiseFulfiller;
friend class promiseInternal::PromiseState<T>;
};
inline void PromiseFulfiller<void>::Callback::fulfill(Promise<void> chain) {
state->fulfill(chain.release());
}
namespace promiseInternal {
template <typename T>
void PromiseState<T>::fulfill(Promise<T> chainedPromise) {
auto listener = this->listener;
auto owner = this->owner;
if (owner == nullptr) {
throw std::logic_error(
"Not supported: Calling fulfill() with a chained promise from the PromiseFulfiller's "
"constructor. This is probably not what you intended anyway.");
} else {
*owner = chainedPromise.release(); // will delete this!
if (listener != nullptr) {
owner->state->setListener(listener);
}
}
}
inline void PromiseState<void>::fulfill(Promise<void> chainedPromise) {
auto listener = this->listener;
auto owner = this->owner;
if (owner == nullptr) {
throw std::logic_error(
"Not supported: Calling fulfill() with a chained promise from the PromiseFulfiller's "
"constructor. This is probably not what you intended anyway.");
} else {
*owner = chainedPromise.release(); // will delete this!
if (listener != nullptr) {
owner->state->setListener(listener);
}
}
}
struct PromiseConstructors {
template <typename PromiseFulfillerImpl, typename... Params>
static Promise<typename PromiseFulfillerImpl::PromiseType> newPromise(Params&&... params) {
typedef typename PromiseFulfillerImpl::PromiseType T;
return Promise<T>(newOwned<promiseInternal::FulfillerPromiseState<T, PromiseFulfillerImpl>>(
std::forward<Params>(params)...));
}
template <typename T>
static Promise<typename std::remove_reference<T>::type> newFulfilledPromise(T&& value) {
Promise<T> result(newOwned<promiseInternal::PromiseState<T>>());
result.state->fulfill(std::forward<T>(value));
return result;
}
static Promise<void> newFulfilledPromise() {
Promise<void> result(newOwned<promiseInternal::PromiseState<void>>());
result.state->fulfill();
return result;
}
template <typename T>
static Promise<T> newPromiseFromCurrentException() {
Promise<T> result(newOwned<promiseInternal::PromiseState<T>>());
result.state->propagateCurrentException();
return result;
}
};
} // namespace promiseInternal
template <typename PromiseFulfillerImpl, typename... Params>
Promise<typename PromiseFulfillerImpl::PromiseType> newPromise(Params&&... params) {
return promiseInternal::PromiseConstructors::newPromise<PromiseFulfillerImpl, Params...>(
std::forward<Params>(params)...);
}
template <typename T>
Promise<typename std::remove_reference<T>::type> newFulfilledPromise(T&& value) {
return promiseInternal::PromiseConstructors::newFulfilledPromise(std::forward<T>(value));
}
inline Promise<void> newFulfilledPromise() {
return promiseInternal::PromiseConstructors::newFulfilledPromise();
}
template <typename T>
Promise<T> newPromiseFromCurrentException() {
return promiseInternal::PromiseConstructors::newPromiseFromCurrentException<T>();
}
// =======================================================================================
template <typename... Types>
class Executor::When {
typedef ValuePack<Types...> ParamPack;
public:
When(const When& other) = delete;
When(When&& other) = default;
When& operator=(const When& other) = delete;
When& operator=(When&& other) = delete;
#define RESULT_TYPE \
typename promiseInternal::Chain< \
decltype(func(std::declval<typename promiseInternal::Unpack<Types>::Type>()...))>::Type
template <typename Func>
auto operator()(Func&& func) -> Promise<RESULT_TYPE> {
typedef RESULT_TYPE ResultType;
return operator()(std::move(func), promiseInternal::ForceErrorFunctor<ResultType>());
}
template <typename Func, typename ExceptionHandler>
auto operator()(Func&& func, ExceptionHandler&& exceptionHandler) -> Promise<RESULT_TYPE> {
typedef RESULT_TYPE ResultType;
typedef promiseInternal::DependentPromiseFulfiller<
ResultType, Func, ExceptionHandler, ParamPack> Fulfiller;
return newPromise<Fulfiller>(
executor, std::move(func), std::move(exceptionHandler), std::move(params));
}
#undef RESULT_TYPE
private:
Executor* executor;
ParamPack params;
When(Executor* executor, Types&&... params)
: executor(executor),
params(std::move(params)...) {}
friend class Executor;
};
template <typename... Types>
Executor::When<typename std::remove_reference<Types>::type...> Executor::when(Types&&... params) {
return When<typename std::remove_reference<Types>::type...>(this, std::move(params)...);
}
} // namespace ekam
#endif // KENTONSCODE_BASE_PROMISE_H_
================================================
FILE: src/base/Promise_test.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Promise.h"
#include <stdio.h>
#include <stdlib.h>
#include <kj/compat/gtest.h>
namespace ekam {
namespace {
class MockExecutor: public Executor {
private:
class PendingRunnableImpl : public PendingRunnable {
public:
PendingRunnableImpl(MockExecutor* executor, OwnedPtr<Runnable> runnable)
: executor(executor), runnable(runnable.release()) {}
~PendingRunnableImpl() {
if (runnable != nullptr) {
for (auto iter = executor->queue.begin(); iter != executor->queue.end(); ++iter) {
if (*iter == this) {
executor->queue.erase(iter);
break;
}
}
}
}
void run() {
runnable.release()->run();
}
private:
MockExecutor* executor;
OwnedPtr<Runnable> runnable;
};
public:
MockExecutor() {}
~MockExecutor() {
EXPECT_TRUE(queue.empty());
}
void runNext() {
ASSERT_FALSE(queue.empty());
auto front = queue.front();
queue.pop_front();
front->run();
}
bool empty() {
return queue.empty();
}
// implements Executor -----------------------------------------------------------------
OwnedPtr<PendingRunnable> runLater(OwnedPtr<Runnable> runnable) {
auto result = newOwned<PendingRunnableImpl>(this, runnable.release());
queue.push_back(result.get());
return result.release();
}
private:
std::deque<PendingRunnableImpl*> queue;
};
template <typename T>
class MockPromiseFulfiller: public PromiseFulfiller<T> {
public:
typedef typename PromiseFulfiller<T>::Callback Callback;
MockPromiseFulfiller(Callback* callback, Callback** callbackPtr)
: callbackPtr(callbackPtr) {
*callbackPtr = callback;
}
~MockPromiseFulfiller() {
*callbackPtr = nullptr;
}
private:
Callback** callbackPtr;
};
TEST(PromiseTest, Basic) {
MockExecutor mockExecutor;
PromiseFulfiller<int>::Callback* fulfiller;
auto promise = newPromise<MockPromiseFulfiller<int>>(&fulfiller);
bool triggered = false;
Promise<int> promise2 = mockExecutor.when(promise)(
[&triggered](int i) -> int {
EXPECT_EQ(5, i);
triggered = true;
return 123;
});
EXPECT_FALSE(triggered);
fulfiller->fulfill(5);
EXPECT_FALSE(triggered);
mockExecutor.runNext();
EXPECT_TRUE(triggered);
// Fulfiller deleted because promise has been consumed.
EXPECT_TRUE(fulfiller == nullptr);
}
TEST(PromiseTest, PreFulfilled) {
MockExecutor mockExecutor;
auto promise = newFulfilledPromise(5);
bool triggered = false;
Promise<int> promise2 = mockExecutor.when(promise)(
[&triggered](int i) -> int {
EXPECT_EQ(5, i);
triggered = true;
return 123;
});
EXPECT_FALSE(triggered);
mockExecutor.runNext();
EXPECT_TRUE(triggered);
}
TEST(PromiseTest, Dependent) {
MockExecutor mockExecutor;
PromiseFulfiller<int>::Callback* fulfiller1;
auto promise1 = newPromise<MockPromiseFulfiller<int>>(&fulfiller1);
PromiseFulfiller<int>::Callback* fulfiller2;
auto promise2 = newPromise<MockPromiseFulfiller<int>>(&fulfiller2);
Promise<int> promise3 = mockExecutor.when(promise1, promise2)(
[](int a, int b) -> int {
return a + b;
});
int result = 0;
Promise<void> promise4 = mockExecutor.when(promise3)(
[&result](int a) {
result = a;
});
EXPECT_TRUE(mockExecutor.empty());
fulfiller1->fulfill(12);
EXPECT_TRUE(mockExecutor.empty());
fulfiller2->fulfill(34);
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
EXPECT_EQ(result, 46);
}
TEST(PromiseTest, Chained) {
MockExecutor mockExecutor;
PromiseFulfiller<int>::Callback* fulfiller1;
auto promise1 = newPromise<MockPromiseFulfiller<int>>(&fulfiller1);
PromiseFulfiller<int>::Callback* fulfiller2;
auto promise2 = newPromise<MockPromiseFulfiller<int>>(&fulfiller2);
int result = 0;
Promise<void> promise3 = mockExecutor.when(promise2)(
[&result](int a) {
result = a;
});
EXPECT_TRUE(mockExecutor.empty());
fulfiller2->fulfill(promise1.release());
EXPECT_TRUE(mockExecutor.empty());
EXPECT_EQ(0, result);
fulfiller1->fulfill(123);
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
EXPECT_EQ(result, 123);
}
TEST(PromiseTest, ChainedVoid) {
MockExecutor mockExecutor;
PromiseFulfiller<void>::Callback* fulfiller1;
auto promise1 = newPromise<MockPromiseFulfiller<void>>(&fulfiller1);
PromiseFulfiller<void>::Callback* fulfiller2;
auto promise2 = newPromise<MockPromiseFulfiller<void>>(&fulfiller2);
bool triggered = false;
Promise<void> promise3 = mockExecutor.when(promise2)(
[&triggered](Void) {
triggered = true;
});
EXPECT_TRUE(mockExecutor.empty());
fulfiller2->fulfill(promise1.release());
EXPECT_TRUE(mockExecutor.empty());
EXPECT_FALSE(triggered);
fulfiller1->fulfill();
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
EXPECT_TRUE(triggered);
}
TEST(PromiseTest, ChainedVoidWhen) {
MockExecutor mockExecutor;
PromiseFulfiller<void>::Callback* fulfiller1;
auto promise1 = newPromise<MockPromiseFulfiller<void>>(&fulfiller1);
PromiseFulfiller<void>::Callback* fulfiller2 = nullptr;
Promise<void> promise3 = mockExecutor.when(promise1)(
[&fulfiller2](Void) -> Promise<void> {
DEBUG_ERROR << "promise3";
return newPromise<MockPromiseFulfiller<void>>(&fulfiller2);
});
bool triggered = false;
Promise<void> promise4 = mockExecutor.when(promise3)(
[&triggered](Void) {
DEBUG_ERROR << "promise4";
triggered = true;
});
EXPECT_TRUE(mockExecutor.empty());
fulfiller1->fulfill();
EXPECT_FALSE(mockExecutor.empty());
EXPECT_TRUE(fulfiller2 == nullptr);
mockExecutor.runNext();
EXPECT_FALSE(fulfiller2 == nullptr);
EXPECT_TRUE(mockExecutor.empty());
EXPECT_FALSE(triggered);
ASSERT_TRUE(fulfiller2 != nullptr);
fulfiller2->fulfill();
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
EXPECT_TRUE(triggered);
}
TEST(PromiseTest, ChainedPreFulfilled) {
MockExecutor mockExecutor;
auto promise1 = newFulfilledPromise(123);
PromiseFulfiller<int>::Callback* fulfiller2;
auto promise2 = newPromise<MockPromiseFulfiller<int>>(&fulfiller2);
int result = 0;
Promise<void> promise3 = mockExecutor.when(promise2)(
[&result](int a) {
result = a;
});
EXPECT_TRUE(mockExecutor.empty());
fulfiller2->fulfill(promise1.release());
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
ASSERT_EQ(result, 123);
}
TEST(PromiseTest, MoveSemantics) {
MockExecutor mockExecutor;
PromiseFulfiller<OwnedPtr<int>>::Callback* fulfiller;
auto promise = newPromise<MockPromiseFulfiller<OwnedPtr<int>>>(&fulfiller);
OwnedPtr<int> ptr = newOwned<int>(12);
int result = 0;
Promise<void> promise2 = mockExecutor.when(promise, ptr)(
[&result](OwnedPtr<int> i, OwnedPtr<int> j) {
result = *i + *j;
});
fulfiller->fulfill(newOwned<int>(34));
mockExecutor.runNext();
ASSERT_EQ(result, 46);
}
TEST(PromiseTest, Cancel) {
MockExecutor mockExecutor;
PromiseFulfiller<int>::Callback* fulfiller;
auto promise = newPromise<MockPromiseFulfiller<int>>(&fulfiller);
Promise<void> promise2 = mockExecutor.when(promise)(
[](int i) {
ADD_FAILURE() << "Can't get here.";
});
EXPECT_TRUE(mockExecutor.empty());
fulfiller->fulfill(5);
EXPECT_FALSE(mockExecutor.empty());
promise2.release();
EXPECT_TRUE(mockExecutor.empty());
}
TEST(PromiseTest, VoidPromise) {
MockExecutor mockExecutor;
PromiseFulfiller<void>::Callback* fulfiller;
auto promise = newPromise<MockPromiseFulfiller<void>>(&fulfiller);
bool triggered = false;
Promise<void> promise2 = mockExecutor.when(promise)(
[&triggered](Void) {
triggered = true;
});
EXPECT_FALSE(triggered);
fulfiller->fulfill();
EXPECT_FALSE(triggered);
mockExecutor.runNext();
EXPECT_TRUE(triggered);
}
TEST(PromiseTest, Exception) {
MockExecutor mockExecutor;
PromiseFulfiller<int>::Callback* fulfiller1;
auto promise1 = newPromise<MockPromiseFulfiller<int>>(&fulfiller1);
PromiseFulfiller<int>::Callback* fulfiller2;
auto promise2 = newPromise<MockPromiseFulfiller<int>>(&fulfiller2);
bool triggered = false;
Promise<void> promise3 = mockExecutor.when(promise1, promise2, 123)(
[](int i, int j, int k) {
ADD_FAILURE() << "Can't get here.";
}, [&triggered](MaybeException<int> i, MaybeException<int> j, int k) {
triggered = true;
ASSERT_TRUE(i.isException());
ASSERT_FALSE(j.isException());
ASSERT_EQ(123, k);
ASSERT_EQ(456, j.get());
try {
i.get();
ADD_FAILURE() << "Expected exception.";
} catch (const std::logic_error& e) {
ASSERT_STREQ("test", e.what());
}
});
try {
throw std::logic_error("test");
} catch (...) {
fulfiller1->propagateCurrentException();
}
fulfiller2->fulfill(456);
EXPECT_FALSE(triggered);
mockExecutor.runNext();
EXPECT_TRUE(triggered);
}
TEST(PromiseTest, ExceptionInCallback) {
MockExecutor mockExecutor;
PromiseFulfiller<int>::Callback* fulfiller1;
auto promise1 = newPromise<MockPromiseFulfiller<int>>(&fulfiller1);
Promise<int> promise2 = mockExecutor.when(promise1)(
[](int a) -> int {
throw std::logic_error("test");
});
bool triggered = false;
Promise<void> promise3 = mockExecutor.when(promise2)(
[](int i) {
ADD_FAILURE() << "Can't get here.";
}, [&triggered](MaybeException<int> i) {
triggered = true;
ASSERT_TRUE(i.isException());
try {
i.get();
ADD_FAILURE() << "Expected exception.";
} catch (const std::logic_error& e) {
ASSERT_STREQ("test", e.what());
}
});
EXPECT_TRUE(mockExecutor.empty());
fulfiller1->fulfill(12);
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
ASSERT_TRUE(triggered);
}
TEST(PromiseTest, ExceptionPropagation) {
MockExecutor mockExecutor;
PromiseFulfiller<int>::Callback* fulfiller1;
auto promise1 = newPromise<MockPromiseFulfiller<int>>(&fulfiller1);
Promise<void> promise2 = mockExecutor.when(promise1)(
[](int a) {
ADD_FAILURE() << "Can't get here.";
});
bool triggered = false;
Promise<void> promise3 = mockExecutor.when(promise2)(
[](Void) {
ADD_FAILURE() << "Can't get here.";
}, [&triggered](MaybeException<void> i) {
triggered = true;
ASSERT_TRUE(i.isException());
try {
i.get();
ADD_FAILURE() << "Expected exception.";
} catch (const std::logic_error& e) {
ASSERT_STREQ("test", e.what());
}
});
EXPECT_TRUE(mockExecutor.empty());
try {
throw std::logic_error("test");
} catch (...) {
fulfiller1->propagateCurrentException();
}
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
EXPECT_FALSE(mockExecutor.empty());
mockExecutor.runNext();
ASSERT_TRUE(triggered);
}
} // namespace
} // namespace ekam
================================================
FILE: src/base/Table.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef KENTONSCODE_BASE_TABLE_H_
#define KENTONSCODE_BASE_TABLE_H_
#include <unordered_map>
#include <vector>
#include <stdlib.h>
namespace ekam {
// =======================================================================================
// Internal helpers. Please ignore.
template <typename Key, typename Value>
class DummyMap {
public:
typedef std::pair<const Key, Value> value_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
inline iterator begin() { return NULL; }
inline iterator end() { return NULL; }
inline const_iterator begin() const { return NULL; }
inline const_iterator end() const { return NULL; }
inline iterator insert(const value_type& value) {
return NULL;
}
inline int erase(const iterator& iter) {
return 0;
}
inline void swap(DummyMap& other) {}
};
template <typename Choices, int index>
struct ChooseType;
template <typename Choices>
struct ChooseType<Choices, 0> : public Choices::Choice0 {};
template <typename Choices>
struct ChooseType<Choices, 1> : public Choices::Choice1 {};
template <typename Choices>
struct ChooseType<Choices, 2> : public Choices::Choice2 {};
// =======================================================================================
template <typename T, typename Hasher = std::hash<T>, typename Eq = std::equal_to<T> >
struct IndexedColumn {
typedef T Value;
typedef std::unordered_multimap<T, int, Hasher, Eq> Index;
};
template <typename T, typename Hasher = std::hash<T>, typename Eq = std::equal_to<T> >
struct UniqueColumn {
typedef T Value;
typedef std::unordered_map<T, int, Hasher, Eq> Index;
};
template <typename T, typename Hasher = std::hash<T>, typename Eq = std::equal_to<T> >
struct Column {
typedef T Value;
typedef DummyMap<T, int> Index;
};
struct EmptyColumn {
struct Value {};
typedef DummyMap<Value, int> Index;
};
template <typename Column0, typename Column1 = EmptyColumn, typename Column2 = EmptyColumn>
class Table {
private:
struct Columns {
#define CHOICE(INDEX) \
struct Choice##INDEX : public Column##INDEX { \
static inline typename Column##INDEX::Index* index(Table* table) { \
return &table->index##INDEX; \
} \
static inline const typename Column##INDEX::Index& index(const Table& table) { \
return table.index##INDEX; \
} \
template <typename Row> \
static inline const typename Column##INDEX::Value& cell(const Row& row) { \
return row.cell##INDEX; \
} \
};
CHOICE(0)
CHOICE(1)
CHOICE(2)
#undef CHOICE
};
template <int columnNumber>
class Column : public ChooseType<Columns, columnNumber> {};
public:
Table() : deletedCount(0) {}
~Table() {}
class Row {
public:
inline Row() {}
template <int columnNumber>
inline const typename Column<columnNumber>::Value& cell() const {
return Column<columnNumber>::cell(*this);
}
private:
typename Column<0>::Value cell0;
typename Column<1>::Value cell1;
typename Column<2>::Value cell2;
bool deleted;
inline Row(const typename Column<0>::Value& cell0,
const typename Column<1>::Value& cell1,
const typename Column<2>::Value& cell2)
: cell0(cell0), cell1(cell1), cell2(cell2), deleted(false) {}
friend class Table;
};
class RowIterator {
public:
inline RowIterator() {}
RowIterator(const Table& table)
: current(NULL), nextIter(table.rows.begin()), end(table.rows.end()) {}
bool next() {
while (true) {
if (nextIter == end) {
return false;
} else if (!nextIter->deleted) {
current = &*nextIter;
++nextIter;
return true;
}
++nextIter;
}
}
template <int columnNumber>
inline const typename Column<columnNumber>::Value& cell() const {
return Column<columnNumber>::cell(*current);
}
private:
const Row* current;
typename std::vector<Row>::const_iterator nextIter;
const typename std::vector<Row>::const_iterator end;
};
template <int columnNumber>
class SearchIterator {
public:
inline SearchIterator() {}
SearchIterator(const Table& table, const typename Column<columnNumber>::Value& value)
: table(table), current(NULL),
range(Column<columnNumber>::index(table).equal_range(value)) {}
inline bool next() {
while (true) {
if (range.first == range.second) {
return false;
}
const Row* row = &table.rows[range.first->second];
if (!row->deleted) {
current = row;
++range.first;
return true;
}
++range.first;
}
}
template <int cellColumnNumber>
inline const typename Column<cellColumnNumber>::Value& cell() const {
return Column<cellColumnNumber>::cell(*current);
}
private:
typedef typename Column<columnNumber>::Index::const_iterator InnerIter;
const Table& table;
const Row* current;
std::pair<InnerIter, InnerIter> range;
};
template <int columnNumber>
const Row* find(const typename Column<columnNumber>::Value& value) const {
typename Column<columnNumber>::Index::const_iterator iter =
Column<columnNumber>::index(*this).find(value);
if (iter == Column<columnNumber>::index(*this).end()) {
return NULL;
} else {
const Row* row = &rows[iter->second];
return row->deleted ? NULL : row;
}
}
template <int columnNumber>
const size_t erase(const typename Column<columnNumber>::Value& value) {
typedef typename Column<columnNumber>::Index::iterator ColumnIterator;
std::pair<ColumnIterator, ColumnIterator> range =
Column<columnNumber>::index(this)->equal_range(value);
size_t count = 0;
for (ColumnIterator iter = range.first; iter != range.second; ++iter) {
if (!rows[iter->second].deleted) {
rows[iter->second].deleted = true;
++count;
}
}
deletedCount += count;
if (deletedCount >= 16 && deletedCount > rows.size() / 2) {
refresh();
} else {
// This isn't strictly necessary, but since it's convenient, let's delete the range
// from the index.
Column<columnNumber>::index(this)->erase(range.first, range.second);
}
return count;
}
void add(const typename Column<0>::Value& value0,
const typename Column<1>::Value& value1 = EmptyColumn::Value(),
const typename Column<2>::Value& value2 = EmptyColumn::Value()) {
handleInsertResult(index0.insert(typename Column<0>::Index::value_type(value0, rows.size())));
handleInsertResult(index1.insert(typename Column<1>::Index::value_type(value1, rows.size())));
handleInsertResult(index2.insert(typename Column<2>::Index::value_type(value2, rows.size())));
rows.push_back(Row(value0, value1, value2));
}
template <int columnNumber>
const bool has(const typename Column<columnNumber>::Value& value) {
typedef typename Column<columnNumber>::Index::iterator ColumnIterator;
std::pair<ColumnIterator, ColumnIterator> range =
Column<columnNumber>::index(this)->equal_range(value);
for (ColumnIterator iter = range.first; iter != range.second; ++iter) {
if (!rows[iter->second].deleted) {
return true;
}
}
return false;
}
int size() {
return rows.size() - deletedCount;
}
int capacity() {
return rows.capacity();
}
template <int columnNumber>
int indexSize() {
return Column<columnNumber>::index(this)->size();
}
private:
std::vector<Row> rows;
size_t deletedCount;
typename Column<0>::Index index0;
typename Column<1>::Index index1;
typename Column<2>::Index index2;
void refresh() {
std::vector<int> newLocations;
newLocations.reserve(rows.size());
{
std::vector<Row> newRows;
newRows.reserve(rows.size() - deletedCount);
for (size_t i = 0; i < rows.size(); i++) {
if (rows[i].deleted) {
newLocations.push_back(-1);
} else {
newLocations.push_back(newRows.size());
newRows.push_back(rows[i]);
}
}
rows.swap(newRows);
deletedCount = 0;
}
refreshColumn<0>(newLocations);
refreshColumn<1>(newLocations);
refreshColumn<2>(newLocations);
}
template <int columnNumber>
void refreshColumn(const std::vector<int>& newLocations) {
typedef Column<columnNumber> C;
typedef typename C::Index::iterator ColumnIterator;
typename C::Index newIndex;
for (ColumnIterator iter = C::index(this)->begin();
iter != C::index(this)->end(); ++iter) {
int newLocation = newLocations[iter->second];
if (newLocation != -1) {
newIndex.insert(std::make_pair(iter->first, newLocation));
}
}
C::index(this)->swap(newIndex);
}
template <typename Iterator>
void handleInsertResult(const std::pair<Iterator, bool>& result) {
if (!result.second) {
rows[result.first->second].deleted = true;
result.first->second = rows.size();
}
}
template <typename Iterator>
void handleInsertResult(const Iterator& iter) {}
};
} // namespace ekam
#endif // KENTONSCODE_BASE_TABLE_H_
================================================
FILE: src/base/Table_test.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Table.h"
#include <stdio.h>
#include <stdlib.h>
#include <set>
#include <map>
#include <string>
namespace ekam {
namespace {
#define ASSERT(EXPRESSION) \
if (!(EXPRESSION)) { \
fprintf(stderr, "%s:%d: FAILED: %s\n", __FILE__, __LINE__, #EXPRESSION); \
exit(1); \
}
void testTable() {
{
typedef Table<IndexedColumn<int> > MyTable;
MyTable table;
table.add(1234);
table.add(5678);
const MyTable::Row* row = table.find<0>(1234);
ASSERT(table.find<0>(4321) == NULL);
ASSERT(row != NULL);
ASSERT(row->cell<0>() == 1234);
row = table.find<0>(5678);
ASSERT(row != NULL);
ASSERT(row->cell<0>() == 5678);
{
std::multiset<int> values;
MyTable::RowIterator iter(table);
ASSERT(iter.next());
values.insert(iter.cell<0>());
ASSERT(iter.next());
values.insert(iter.cell<0>());
ASSERT(!iter.next());
ASSERT(values.count(1234) == 1);
ASSERT(values.count(5678) == 1);
}
table.erase<0>(1234);
ASSERT(table.find<0>(4321) == NULL);
ASSERT(table.find<0>(1234) == NULL);
row = table.find<0>(5678);
ASSERT(row != NULL);
ASSERT(row->cell<0>() == 5678);
}
{
typedef Table<UniqueColumn<int>, IndexedColumn<int> > MyTable;
MyTable table;
table.add(12, 34);
table.add(56, 34);
{
std::multiset<int> values;
MyTable::SearchIterator<1> iter(table, 34);
ASSERT(iter.next());
values.insert(iter.cell<0>());
ASSERT(iter.next());
values.insert(iter.cell<0>());
ASSERT(!iter.next());
ASSERT(values.count(12) == 1);
ASSERT(values.count(56) == 1);
}
table.add(12, 78);
const MyTable::Row* row = table.find<0>(12);
ASSERT(row != NULL);
ASSERT(row->cell<0>() == 12);
ASSERT(row->cell<1>() == 78);
{
MyTable::SearchIterator<1> iter(table, 34);
ASSERT(iter.next());
ASSERT(iter.cell<0>() == 56);
ASSERT(!iter.next());
}
}
{
typedef Table<IndexedColumn<std::string>, IndexedColumn<int>, Column<char> > MyTable;
MyTable table;
table.add("foo", 1, 'f');
table.add("foo", 2, 'o');
table.add("foo", 3, 'o');
table.add("bar", 1, 'b');
table.add("bar", 2, 'a');
table.add("bar", 3, 'r');
{
std::map<int, char> values;
MyTable::SearchIterator<0> iter(table, "bar");
ASSERT(iter.next());
ASSERT(iter.cell<0>() == "bar");
values[iter.cell<1>()] = iter.cell<2>();
ASSERT(iter.next());
ASSERT(iter.cell<0>() == "bar");
values[iter.cell<1>()] = iter.cell<2>();
ASSERT(iter.next());
ASSERT(iter.cell<0>() == "bar");
values[iter.cell<1>()] = iter.cell<2>();
ASSERT(!iter.next());
ASSERT(values[1] == 'b');
ASSERT(values[2] == 'a');
ASSERT(values[3] == 'r');
}
{
std::map<std::string, char> values;
MyTable::SearchIterator<1> iter(table, 2);
ASSERT(iter.next());
ASSERT(iter.cell<1>() == 2);
values[iter.cell<0>()] = iter.cell<2>();
ASSERT(iter.next());
ASSERT(iter.cell<1>() == 2);
values[iter.cell<0>()] = iter.cell<2>();
ASSERT(!iter.next());
ASSERT(values["foo"] == 'o');
ASSERT(values["bar"] == 'a');
}
}
{
typedef Table<IndexedColumn<int>, IndexedColumn<int> > MyTable;
MyTable table;
for (int i = 0; i < 50; i++) {
table.add(123, i);
table.add(456, i);
table.add(789, i);
}
ASSERT(table.size() == 150);
ASSERT(table.capacity() >= 150);
ASSERT(table.indexSize<0>() == 150);
ASSERT(table.indexSize<1>() == 150);
table.erase<0>(123);
ASSERT(table.size() == 100);
ASSERT(table.capacity() >= 150);
ASSERT(table.indexSize<0>() == 100);
ASSERT(table.indexSize<1>() == 150);
table.erase<0>(456);
ASSERT(table.size() == 50);
ASSERT(table.capacity() == 50);
ASSERT(table.indexSize<0>() == 50);
ASSERT(table.indexSize<1>() == 50);
const MyTable::Row* row = table.find<1>(5);
ASSERT(row != NULL);
ASSERT(row->cell<0>() == 789);
{
std::multiset<int> values;
MyTable::SearchIterator<0> iter(table, 789);
for (int i = 0; i < 50; i++) {
ASSERT(iter.next());
values.insert(iter.cell<1>());
}
ASSERT(!iter.next());
for (int i = 0; i < 50; i++) {
ASSERT(values.count(i) == 1);
}
}
ASSERT(!MyTable::SearchIterator<0>(table, 123).next());
ASSERT(!MyTable::SearchIterator<0>(table, 456).next());
{
std::multiset<int> values0;
std::multiset<int> values1;
MyTable::RowIterator iter(table);
for (int i = 0; i < 50; i++) {
ASSERT(iter.next());
values0.insert(iter.cell<0>());
values1.insert(iter.cell<1>());
}
ASSERT(!iter.next());
ASSERT(values0.count(789) == 50);
for (int i = 0; i < 50; i++) {
ASSERT(values1.count(i) == 1);
}
}
}
}
} // namespace
} // namespace ekam
int main(int argc, char* argv[]) {
ekam::testTable();
return 0;
}
================================================
FILE: src/base/sha256.cpp
================================================
/********************************************************************
* Pieces of endian.h from FreeBSD *
********************************************************************/
/*-
* Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/sys/sys/endian.h,v 1.6.32.1 2009/08/03 08:13:06 kensmith Exp $
*/
#include <stdint.h>
inline void
be32enc(void *pp, uint32_t u)
{
unsigned char *p = (unsigned char *)pp;
p[0] = (u >> 24) & 0xff;
p[1] = (u >> 16) & 0xff;
p[2] = (u >> 8) & 0xff;
p[3] = u & 0xff;
}
inline uint32_t
be32dec(const void *pp)
{
unsigned char const *p = (unsigned char const *)pp;
return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
}
/********************************************************************
* sha256c.c from FreeBSD's libmd *
* Modified to compile as C++ and placed in ekam namespace. *
********************************************************************/
/*-
* Copyright 2005 Colin Percival
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libmd/sha256c.c,v 1.2.10.1 2009/08/03 08:13:06 kensmith Exp $");
*/
#include <sys/types.h>
#include <string.h>
#include "sha256.h"
namespace ekam {
#if BYTE_ORDER == BIG_ENDIAN
/* Copy a vector of big-endian uint32_t into a vector of bytes */
#define be32enc_vect(dst, src, len) \
memcpy((void *)dst, (const void *)src, (size_t)len)
/* Copy a vector of bytes into a vector of big-endian uint32_t */
#define be32dec_vect(dst, src, len) \
memcpy((void *)dst, (const void *)src, (size_t)len)
#else /* BYTE_ORDER != BIG_ENDIAN */
/*
* Encode a length len/4 vector of (uint32_t) into a length len vector of
* (unsigned char) in big-endian form. Assumes len is a multiple of 4.
*/
static void
be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
{
size_t i;
for (i = 0; i < len / 4; i++)
be32enc(dst + i * 4, src[i]);
}
/*
* Decode a big-endian length len vector of (unsigned char) into a length
* len/4 vector of (uint32_t). Assumes len is a multiple of 4.
*/
static void
be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
{
size_t i;
for (i = 0; i < len / 4; i++)
dst[i] = be32dec(src + i * 4);
}
#endif /* BYTE_ORDER != BIG_ENDIAN */
/* Elementary functions used by SHA256 */
#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
#define Maj(x, y, z) ((x & (y | z)) | (y & z))
#define SHR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
/* SHA256 round function */
#define RND(a, b, c, d, e, f, g, h, k) \
t0 = h + S1(e) + Ch(e, f, g) + k; \
t1 = S0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
/* Adjusted round function for rotating state */
#define RNDr(S, W, i, k) \
RND(S[(64 - i) % 8], S[(65 - i) % 8], \
S[(66 - i) % 8], S[(67 - i) % 8], \
S[(68 - i) % 8], S[(69 - i) % 8], \
S[(70 - i) % 8], S[(71 - i) % 8], \
W[i] + k)
/*
* SHA256 block compression function. The 256-bit state is transformed via
* the 512-bit input block to produce a new state.
*/
static void
SHA256_Transform(uint32_t * state, const unsigned char block[64])
{
uint32_t W[64];
uint32_t S[8];
uint32_t t0, t1;
int i;
/* 1. Prepare message schedule W. */
be32dec_vect(W, block, 64);
for (i = 16; i < 64; i++)
W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
/* 2. Initialize working variables. */
memcpy(S, state, 32);
/* 3. Mix. */
RNDr(S, W, 0, 0x428a2f98);
RNDr(S, W, 1, 0x71374491);
RNDr(S, W, 2, 0xb5c0fbcf);
RNDr(S, W, 3, 0xe9b5dba5);
RNDr(S, W, 4, 0x3956c25b);
RNDr(S, W, 5, 0x59f111f1);
RNDr(S, W, 6, 0x923f82a4);
RNDr(S, W, 7, 0xab1c5ed5);
RNDr(S, W, 8, 0xd807aa98);
RNDr(S, W, 9, 0x12835b01);
RNDr(S, W, 10, 0x243185be);
RNDr(S, W, 11, 0x550c7dc3);
RNDr(S, W, 12, 0x72be5d74);
RNDr(S, W, 13, 0x80deb1fe);
RNDr(S, W, 14, 0x9bdc06a7);
RNDr(S, W, 15, 0xc19bf174);
RNDr(S, W, 16, 0xe49b69c1);
RNDr(S, W, 17, 0xefbe4786);
RNDr(S, W, 18, 0x0fc19dc6);
RNDr(S, W, 19, 0x240ca1cc);
RNDr(S, W, 20, 0x2de92c6f);
RNDr(S, W, 21, 0x4a7484aa);
RNDr(S, W, 22, 0x5cb0a9dc);
RNDr(S, W, 23, 0x76f988da);
RNDr(S, W, 24, 0x983e5152);
RNDr(S, W, 25, 0xa831c66d);
RNDr(S, W, 26, 0xb00327c8);
RNDr(S, W, 27, 0xbf597fc7);
RNDr(S, W, 28, 0xc6e00bf3);
RNDr(S, W, 29, 0xd5a79147);
RNDr(S, W, 30, 0x06ca6351);
RNDr(S, W, 31, 0x14292967);
RNDr(S, W, 32, 0x27b70a85);
RNDr(S, W, 33, 0x2e1b2138);
RNDr(S, W, 34, 0x4d2c6dfc);
RNDr(S, W, 35, 0x53380d13);
RNDr(S, W, 36, 0x650a7354);
RNDr(S, W, 37, 0x766a0abb);
RNDr(S, W, 38, 0x81c2c92e);
RNDr(S, W, 39, 0x92722c85);
RNDr(S, W, 40, 0xa2bfe8a1);
RNDr(S, W, 41, 0xa81a664b);
RNDr(S, W, 42, 0xc24b8b70);
RNDr(S, W, 43, 0xc76c51a3);
RNDr(S, W, 44, 0xd192e819);
RNDr(S, W, 45, 0xd6990624);
RNDr(S, W, 46, 0xf40e3585);
RNDr(S, W, 47, 0x106aa070);
RNDr(S, W, 48, 0x19a4c116);
RNDr(S, W, 49, 0x1e376c08);
RNDr(S, W, 50, 0x2748774c);
RNDr(S, W, 51, 0x34b0bcb5);
RNDr(S, W, 52, 0x391c0cb3);
RNDr(S, W, 53, 0x4ed8aa4a);
RNDr(S, W, 54, 0x5b9cca4f);
RNDr(S, W, 55, 0x682e6ff3);
RNDr(S, W, 56, 0x748f82ee);
RNDr(S, W, 57, 0x78a5636f);
RNDr(S, W, 58, 0x84c87814);
RNDr(S, W, 59, 0x8cc70208);
RNDr(S, W, 60, 0x90befffa);
RNDr(S, W, 61, 0xa4506ceb);
RNDr(S, W, 62, 0xbef9a3f7);
RNDr(S, W, 63, 0xc67178f2);
/* 4. Mix local working variables into global state */
for (i = 0; i < 8; i++)
state[i] += S[i];
}
static unsigned char PAD[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* Add padding and terminating bit-count. */
static void
SHA256_Pad(SHA256_CTX * ctx)
{
unsigned char len[8];
uint32_t r, plen;
/*
* Convert length to a vector of bytes -- we do this now rather
* than later because the length will change after we pad.
*/
be32enc_vect(len, ctx->count, 8);
/* Add 1--64 bytes so that the resulting length is 56 mod 64 */
r = (ctx->count[1] >> 3) & 0x3f;
plen = (r < 56) ? (56 - r) : (120 - r);
SHA256_Update(ctx, PAD, (size_t)plen);
/* Add the terminating bit-count */
SHA256_Update(ctx, len, 8);
}
/* SHA-256 initialization. Begins a SHA-256 operation. */
void
SHA256_Init(SHA256_CTX * ctx)
{
/* Zero bits processed so far */
ctx->count[0] = ctx->count[1] = 0;
/* Magic initialization constants */
ctx->state[0] = 0x6A09E667;
ctx->state[1] = 0xBB67AE85;
ctx->state[2] = 0x3C6EF372;
ctx->state[3] = 0xA54FF53A;
ctx->state[4] = 0x510E527F;
ctx->state[5] = 0x9B05688C;
ctx->state[6] = 0x1F83D9AB;
ctx->state[7] = 0x5BE0CD19;
}
/* Add bytes into the hash */
void
SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
{
uint32_t bitlen[2];
uint32_t r;
const unsigned char *src = reinterpret_cast<const unsigned char*>(in);
/* Number of bytes left in the buffer from previous updates */
r = (ctx->count[1] >> 3) & 0x3f;
/* Convert the length into a number of bits */
bitlen[1] = ((uint32_t)len) << 3;
bitlen[0] = (uint32_t)(len >> 29);
/* Update number of bits */
if ((ctx->count[1] += bitlen[1]) < bitlen[1])
ctx->count[0]++;
ctx->count[0] += bitlen[0];
/* Handle the case where we don't need to perform any transforms */
if (len < 64 - r) {
memcpy(&ctx->buf[r], src, len);
return;
}
/* Finish the current block */
memcpy(&ctx->buf[r], src, 64 - r);
SHA256_Transform(ctx->state, ctx->buf);
src += 64 - r;
len -= 64 - r;
/* Perform complete blocks */
while (len >= 64) {
SHA256_Transform(ctx->state, src);
src += 64;
len -= 64;
}
/* Copy left over data into buffer */
memcpy(ctx->buf, src, len);
}
/*
* SHA-256 finalization. Pads the input data, exports the hash value,
* and clears the context state.
*/
void
SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
{
/* Add padding */
SHA256_Pad(ctx);
/* Write the hash */
be32enc_vect(digest, ctx->state, 32);
/* Clear the context state */
memset((void *)ctx, 0, sizeof(*ctx));
}
} // namespace ekam
================================================
FILE: src/base/sha256.h
================================================
/********************************************************************
* sha256.h from FreeBSD's libmd *
* Modified to compile as C++ and placed in ekam namespace. *
********************************************************************/
/*-
* Copyright 2005 Colin Percival
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/lib/libmd/sha256.h,v 1.2.10.1 2009/08/03 08:13:06 kensmith Exp $
*/
#ifndef _SHA256_H_
#define _SHA256_H_
#include <sys/types.h>
namespace ekam {
typedef struct SHA256Context {
uint32_t state[8];
uint32_t count[2];
unsigned char buf[64];
} SHA256_CTX;
void SHA256_Init(SHA256_CTX *);
void SHA256_Update(SHA256_CTX *, const void *, size_t);
void SHA256_Final(unsigned char [32], SHA256_CTX *);
char *SHA256_End(SHA256_CTX *, char *);
char *SHA256_File(const char *, char *);
char *SHA256_FileChunk(const char *, char *, off_t, off_t);
char *SHA256_Data(const void *, unsigned int, char *);
} // namespace ekam
#endif /* !_SHA256_H_ */
================================================
FILE: src/ekam/Action.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Action.h"
namespace ekam {
BuildContext::~BuildContext() noexcept(false) {}
Action::~Action() {}
ActionFactory::~ActionFactory() {}
const int BuildContext::INSTALL_LOCATION_COUNT;
const char* const BuildContext::INSTALL_LOCATION_NAMES[INSTALL_LOCATION_COUNT] = {
"bin", "lib", "node_modules"
};
} // namespace ekam
================================================
FILE: src/ekam/Action.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef KENTONSCODE_EKAM_ACTION_H_
#define KENTONSCODE_EKAM_ACTION_H_
#include <string>
#include <vector>
#include <sys/types.h>
#include "base/OwnedPtr.h"
#include "os/File.h"
#include "Tag.h"
#include "os/EventManager.h"
namespace ekam {
class ActionFactory;
class ProcessExitCallback {
public:
virtual ~ProcessExitCallback();
// Negative = signal number.
virtual void done(int exit_status) = 0;
};
class BuildContext {
public:
virtual ~BuildContext() noexcept(false);
virtual File* findProvider(Tag id) = 0;
virtual File* findInput(const std::string& path) = 0;
enum InstallLocation {
BIN,
LIB,
NODE_MODULES
};
static const int INSTALL_LOCATION_COUNT = 3;
static const char* const INSTALL_LOCATION_NAMES[INSTALL_LOCATION_COUNT];
virtual void provide(File* file, const std::vector<Tag>& tags) = 0;
virtual void install(File* file, InstallLocation location, const std::string& name) = 0;
virtual void log(const std::string& text) = 0;
virtual OwnedPtr<File> newOutput(const std::string& path) = 0;
virtual void addActionType(OwnedPtr<ActionFactory> factory) = 0;
virtual void passed() = 0;
virtual void failed() = 0;
};
class Action {
public:
virtual ~Action();
virtual bool isSilent() { return false; }
virtual std::string getVerb() = 0;
virtual Promise<void> start(EventManager* eventManager, BuildContext* context) = 0;
};
class ActionFactory {
public:
virtual ~ActionFactory();
virtual void enumerateTriggerTags(std::back_insert_iterator<std::vector<Tag> > iter) = 0;
virtual OwnedPtr<Action> tryMakeAction(const Tag& id, File* file) = 0;
};
} // namespace ekam
#endif // KENTONSCODE_EKAM_ACTION_H_
================================================
FILE: src/ekam/ActionUtil.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ActionUtil.h"
#include <string.h>
namespace ekam {
Logger::Logger(BuildContext* context, OwnedPtr<ByteStream> stream)
: context(context), stream(stream.release()) {}
Logger::~Logger() {}
Promise<void> Logger::run(EventManager* eventManager) {
return eventManager->when(stream->readAsync(eventManager, buffer, sizeof(buffer)))(
[=](size_t size) -> Promise<void> {
if (size == 0) {
return newFulfilledPromise();
}
context->log(std::string(buffer, size));
return run(eventManager);
}, [=](MaybeException<size_t> error) {
try {
error.get();
} catch (const std::exception& e) {
context->log(e.what());
context->failed();
throw;
} catch (...) {
context->log("unknown exception");
context->failed();
throw;
}
});
}
// =======================================================================================
LineReader::LineReader(ByteStream* stream) : stream(stream) {}
LineReader::~LineReader() {}
Promise<OwnedPtr<std::string>> LineReader::readLine(EventManager* eventManager) {
std::string::size_type endpos = leftover.find_first_of('\n');
if (endpos != std::string::npos) {
auto result = newOwned<std::string>(leftover, 0, endpos);
leftover.erase(0, endpos + 1);
return newFulfilledPromise(result.release());
}
return eventManager->when(stream->readAsync(eventManager, buffer, sizeof(buffer)))(
[=](size_t size) -> Promise<OwnedPtr<std::string>> {
if (size == 0) {
if (leftover.empty()) {
// No more data.
return newFulfilledPromise(OwnedPtr<std::string>(nullptr));
} else {
// Still have a line of text that had no trailing newline.
auto line = newOwned<std::string>(std::move(leftover));
leftover.clear();
return newFulfilledPromise(line.release());
}
}
leftover.append(buffer, size);
return readLine(eventManager);
});
}
} // namespace ekam
================================================
FILE: src/ekam/ActionUtil.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef KENTONSCODE_EKAM_ACTIONUTIL_H_
#define KENTONSCODE_EKAM_ACTIONUTIL_H_
#include <string>
#include "os/ByteStream.h"
#include "Action.h"
namespace ekam {
class Logger {
public:
Logger(BuildContext* context, OwnedPtr<ByteStream> stream);
~Logger();
Promise<void> run(EventManager* eventManager);
private:
class ReadAllFulfiller;
BuildContext* context;
OwnedPtr<ByteStream> stream;
char buffer[4096];
};
class LineReader {
public:
LineReader(ByteStream* stream);
~LineReader();
Promise<OwnedPtr<std::string>> readLine(EventManager* eventManager);
private:
ByteStream* stream;
std::string leftover;
char buffer[4096];
};
} // namespace ekam
#endif // KENTONSCODE_EKAM_ACTIONUTIL_H_
================================================
FILE: src/ekam/ConsoleDashboard.cpp
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ConsoleDashboard.h"
#include <sys/ioctl.h>
#include <termios.h>
#include <string.h>
#include <ctype.h>
#include "base/Debug.h"
namespace ekam {
class ConsoleDashboard::LogFormatter {
public:
LogFormatter(const std::string& text)
: text(text.data()), end(text.data() + text.size()), wrapped(false) {
eatWhitespace();
}
inline bool atEnd() {
return text == end;
}
std::string getLine(int startColumn, int windowWidth) {
std::string result;
int column = startColumn;
result.reserve((windowWidth - column) * 2);
if (wrapped) {
result.append(" ");
column += 2;
wrapped = false;
}
while (text < end && *text != '\n' && column < windowWidth) {
if (isalnum(*text)) {
const char* wordEnd = text;
do {
++wordEnd;
} while (wordEnd < end && isalnum(*wordEnd));
int length = wordEnd - text;
if (column + length <= windowWidth) {
// Word fits on this line.
bool isColored = false;
if (strncasecmp(text, "error", length) == 0 ||
strncasecmp(text, "fail", length) == 0 ||
strncasecmp(text, "failed", length) == 0 ||
strncasecmp(text, "fatal", length) == 0) {
result.append(ANSI_COLOR_CODES[RED]);
isColored = true;
} else if (strncasecmp(text, "warning", length) == 0) {
result.append(ANSI_COLOR_CODES[YELLOW]);
isColored = true;
}
result.append(text, wordEnd);
text = wordEnd;
column += length;
if (isColored) {
result.append(ANSI_CLEAR_COLOR);
}
} else if (length >= 20 || column == startColumn) {
// Really long word. Break it.
int spaceAvailable = windowWidth - column;
result.append(text, spaceAvailable);
text += spaceAvailable;
column = windowWidth;
} else {
// Word doesn't fit in remaining space. End the line.
break;
}
} else {
switch (*text) {
case '\t':
column = (column & ~0x7) + 8;
result.push_back(*text);
break;
case '\033': // escape
// ignore -- could be harmful
// TODO: Parse and remove the subsequent terminal instruction to make the output not
// suck. Or better yet, parse the sequence and interpret it. If it is an SGR code
// (e.g. color), pass it through, and just make sure to reset later.
break;
default:
if (*text >= '\0' && *text < ' ') {
// Ignore control character or weird whitespace.
} else {
result.push_back(*text);
++column;
}
break;
}
++text;
}
}
wrapped = !eatWhitespace();
return result;
}
private:
const char* text;
const char* end;
bool wrapped;
// Eat whitespace up to and including a newline. Returns true if a newline was eaten.
bool eatWhitespace() {
while (text < end && isspace(*text) && *text != '\n') {
++text;
}
if (text < end && *text == '\n') {
++text;
return true;
} else {
return false;
}
}
bool tryHighlight(const char* word, Color color, int windowWidth,
int* column, std::string* output) {
int length = strlen(word);
if (windowWidth - *column < length) {
// Don't highlight words broken at line wrapping.
return false;
}
if (end - text < length) {
// Not enough characters left to match word.
return false;
}
if (strncasecmp(text, word, length) != 0) {
// Does not match.
return false;
}
if (end - text > length && isalpha(text[length])) {
// Character after word is a letter. Don't highlight.
return false;
}
if (!output->empty() && isalpha((*output)[output->size() - 1])) {
// Character before word was a letter. Don't highlight.
return false;
}
output->append(ANSI_COLOR_CODES[color]);
output->append(text, text + length);
output->append(ANSI_CLEAR_COLOR);
text += length;
*column += length;
return true;
}
};
class ConsoleDashboard::TaskImpl : public Dashboard::Task {
public:
TaskImpl(ConsoleDashboard* dashboard, const std::string& verb,
const std::string& noun, Silence silence);
~TaskImpl();
// implements Task ---------------------------------------------------------------------
void setState(TaskState state);
void addOutput(const std::string& text);
private:
ConsoleDashboard* dashboard;
TaskState state;
Silence silence;
std::string verb;
std::string noun;
std::string outputText;
void removeFromRunning();
void writeFinalLog(Color verbColor, const char* icon);
friend class ConsoleDashboard;
};
ConsoleDashboard::TaskImpl::TaskImpl(ConsoleDashboard* dashboard,
const std::string& verb, const std::string& noun,
Silence silence)
: dashboard(dashboard), state(PENDING), silence(silence), verb(verb), noun(noun) {}
ConsoleDashboard::TaskImpl::~TaskImpl() {
if (state == RUNNING) {
dashboard->clearRunning();
removeFromRunning();
dashboard->drawRunning();
}
}
void ConsoleDashboard::TaskImpl::setState(TaskState state) {
// If state was previously BLOCKED, and we managed to un-block, then we don't care about the
// reason why we were blocked, so clear the text.
if (this->state == BLOCKED && (state == PENDING || state == RUNNING)) {
outputText.clear();
}
if (this->state == RUNNING) {
if (silence != SILENT) removeFromRunning();
}
this->state = state;
dashboard->clearRunning();
switch (state) {
case PENDING:
// Don't display.
break;
case RUNNING:
if (silence != SILENT) dashboard->runningTasks.push_back(this);
break;
case DONE:
writeFinalLog(DONE_COLOR, " ");
break;
case PASSED:
writeFinalLog(PASSED_COLOR, "✔");
break;
case FAILED:
writeFinalLog(FAILED_COLOR, "✘");
break;
case BLOCKED:
// Don't display.
break;
}
dashboard->drawRunning();
}
void ConsoleDashboard::TaskImpl::addOutput(const std::string& text) {
outputText.append(text);
}
void ConsoleDashboard::TaskImpl::removeFromRunning() {
for (std::vector<TaskImpl*>::iterator iter = dashboard->runningTasks.begin();
iter != dashboard->runningTasks.end(); ++iter) {
if (*iter == this) {
dashboard->runningTasks.erase(iter);
break;
}
}
}
void ConsoleDashboard::TaskImpl::writeFinalLog(Color verbColor, const char* icon) {
// Silent tasks should not be written to the log, unless they had error messages.
if (silence != SILENT || !outputText.empty()) {
fprintf(dashboard->out, "%s%s %s:%s %s\n",
ANSI_COLOR_CODES[verbColor], icon, verb.c_str(), ANSI_CLEAR_COLOR, noun.c_str());
// Write any output we have buffered.
if (!outputText.empty()) {
LogFormatter formatter(outputText);
struct winsize windowSize;
ioctl(dashboard->fd, TIOCGWINSZ, &windowSize);
for (int i = 0; i < dashboard->maxDisplayedLogLines && !formatter.atEnd(); i++) {
std::string line = formatter.getLine(4, windowSize.ws_col);
fprintf(dashboard->out, " %s\n", line.c_str());
}
if (!formatter.atEnd()) {
fprintf(dashboard->out, " ...(log truncated; use -l to increase log limit)...\n");
}
outputText.clear();
}
}
}
// =======================================================================================
const char* const ConsoleDashboard::ANSI_COLOR_CODES[] = {
"\033[30m",
"\033[31m",
"\033[32m",
"\033[33m",
"\033[34m",
"\033[35m",
"\033[36m",
"\033[37m",
"\033[1;30m",
"\033[1;31m",
"\033[1;32m",
"\033[1;33m",
"\033[1;34m",
"\033[1;35m",
"\033[1;36m",
"\033[1;37m"
};
const char* const ConsoleDashboard::ANSI_CLEAR_COLOR = "\033[0m";
// Note: \033[%dF (move cursor up %d lines and to beginning of line) doesn't work on some
// terminals. \033[%dA (move cursor up %d lines) does appear to work, so tack \r on to that
// to go to the beginning of the line.
const char* const ConsoleDashboard::ANSI_MOVE_CURSOR_UP = "\033[%dA\r";
const char* const ConsoleDashboard::ANSI_CLEAR_BELOW_CURSOR = "\033[0J";
const ConsoleDashboard::Color ConsoleDashboard::DONE_COLOR = BRIGHT_BLUE;
const ConsoleDashboard::Color ConsoleDashboard::PASSED_COLOR = BRIGHT_GREEN;
const ConsoleDashboard::Color ConsoleDashboard::FAILED_COLOR = BRIGHT_RED;
const ConsoleDashboard::Color ConsoleDashboard::RUNNING_COLOR = BRIGHT_FUCHSIA;
ConsoleDashboard::ConsoleDashboard(FILE* output, int maxDisplayedLogLines)
: fd(fileno(output)), out(output), maxDisplayedLogLines(maxDisplayedLogLines),
runningTasksLineCount(0), lastDebugMessageCount(DebugMessage::getMessageCount()) {}
ConsoleDashboard::~ConsoleDashboard() {}
OwnedPtr<Dashboard::Task> ConsoleDashboard::beginTask(
const std::string& verb, const std::string& noun, Silence silence) {
return newOwned<TaskImpl>(this, verb, noun, silence);
}
void ConsoleDashboard::clearRunning() {
if (lastDebugMessageCount != DebugMessage::getMessageCount()) {
// Some debug messages were printed. We don't want to clobber them. So we can't clear.
return;
}
if (runningTasksLineCount > 0) {
fprintf(out, ANSI_MOVE_CURSOR_UP, runningTasksLineCount);
fputs(ANSI_CLEAR_BELOW_CURSOR, out);
}
}
void ConsoleDashboard::drawRunning() {
struct winsize windowSize;
ioctl(fd, TIOCGWINSZ, &windowSize);
// Leave a few lines for completed tasks.
int spaceForTasks = windowSize.ws_row - 4;
bool allTasksShown = static_cast<int>(runningTasks.size()) < spaceForTasks;
int displayCount = allTasksShown ? runningTasks.size() : spaceForTasks - 1;
runningTasksLineCount = allTasksShown ? runningTasks.size() : spaceForTasks;
for (int i = 0; i < displayCount; i++) {
TaskImpl* task = runningTasks[i];
int spaceForNoun = windowSize.ws_col - task->verb.size() - 2;
fprintf(out, "%s↺ %s:%s ", ANSI_COLOR_CODES[RUNNING_COLOR],
task->verb.c_str(), ANSI_CLEAR_COLOR);
if (static_cast<int>(task->noun.size()) > spaceForNoun) {
std::string shortenedNoun = task->noun.substr(task->noun.size() - spaceForNoun + 3);
fprintf(out, "...%s", shortenedNoun.c_str());
} else {
fputs(task->noun.c_str(), out);
}
putc('\n', out);
}
if (!allTasksShown) {
fputs("...(more)...\n", out);
}
fflush(out);
lastDebugMessageCount = DebugMessage::getMessageCount();
}
} // namespace ekam
================================================
FILE: src/ekam/ConsoleDashboard.h
================================================
// Ekam Build System
// Author: Kenton Varda (kenton@sandstorm.io)
// Copyright (c) 2010-2015 Kenton Varda, Google Inc., and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy
gitextract_jfckbr5d/
├── .gitignore
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── LICENSE
├── Makefile
├── README.md
├── qtcreator/
│ ├── COPYING.txt
│ ├── EkamDashboard.json.in
│ ├── EkamDashboard.pluginspec.in
│ ├── ekamdashboard.pro
│ ├── ekamdashboard.qrc
│ ├── ekamdashboard_global.h
│ ├── ekamdashboardconstants.h
│ ├── ekamdashboardplugin.cpp
│ ├── ekamdashboardplugin.h
│ ├── ekamtreewidget.cpp
│ └── ekamtreewidget.h
├── src/
│ ├── base/
│ │ ├── Debug.cpp
│ │ ├── Debug.h
│ │ ├── Hash.cpp
│ │ ├── Hash.h
│ │ ├── OwnedPtr.cpp
│ │ ├── OwnedPtr.h
│ │ ├── Promise.cpp
│ │ ├── Promise.h
│ │ ├── Promise_test.cpp
│ │ ├── Table.h
│ │ ├── Table_test.cpp
│ │ ├── sha256.cpp
│ │ └── sha256.h
│ ├── ekam/
│ │ ├── Action.cpp
│ │ ├── Action.h
│ │ ├── ActionUtil.cpp
│ │ ├── ActionUtil.h
│ │ ├── ConsoleDashboard.cpp
│ │ ├── ConsoleDashboard.h
│ │ ├── CppActionFactory.cpp
│ │ ├── CppActionFactory.h
│ │ ├── Dashboard.cpp
│ │ ├── Dashboard.h
│ │ ├── Driver.cpp
│ │ ├── Driver.h
│ │ ├── ExecPluginActionFactory.cpp
│ │ ├── ExecPluginActionFactory.h
│ │ ├── MuxDashboard.cpp
│ │ ├── MuxDashboard.h
│ │ ├── ProtoDashboard.cpp
│ │ ├── ProtoDashboard.h
│ │ ├── SimpleDashboard.cpp
│ │ ├── SimpleDashboard.h
│ │ ├── Tag.cpp
│ │ ├── Tag.h
│ │ ├── dashboard.capnp
│ │ ├── ekam-client.cpp
│ │ ├── ekam-langserve.c++
│ │ ├── ekam.cpp
│ │ ├── ekam.ekam-manifest
│ │ ├── initNetworkDashboardStub.cpp
│ │ ├── langserve.capnp
│ │ └── rules/
│ │ ├── compile.ekam-rule
│ │ ├── include.ekam-rule
│ │ ├── install.ekam-rule
│ │ ├── intercept.c
│ │ ├── intercept.ekam-rule
│ │ ├── proto.ekam-rule
│ │ └── test.ekam-rule
│ └── os/
│ ├── ByteStream.cpp
│ ├── ByteStream.h
│ ├── DiskFile.cpp
│ ├── DiskFile.h
│ ├── EpollEventManager.cpp
│ ├── EpollEventManager.h
│ ├── EventGroup.cpp
│ ├── EventGroup.h
│ ├── EventManager.cpp
│ ├── EventManager.h
│ ├── File.cpp
│ ├── File.h
│ ├── KqueueEventManager.h
│ ├── OsHandle.cpp
│ ├── OsHandle.h
│ ├── PollEventManager.h
│ ├── Socket.cpp
│ ├── Socket.h
│ ├── Subprocess.cpp
│ └── Subprocess.h
└── vscode/
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── src/
│ └── extension.ts
└── tsconfig.json
SYMBOL INDEX (407 symbols across 65 files)
FILE: qtcreator/ekamdashboardconstants.h
function namespace (line 20) | namespace EkamDashboard {
FILE: qtcreator/ekamdashboardplugin.cpp
type EkamDashboard (line 40) | namespace EkamDashboard {
type Internal (line 41) | namespace Internal {
function QString (line 43) | QString toQString(kj::ArrayPtr<const char> str) {
class EkamDashboardPlugin::FakeAsyncInput (line 47) | class EkamDashboardPlugin::FakeAsyncInput final: public kj::AsyncInp...
method read (line 49) | kj::Promise<size_t> read(void* buffer, size_t minBytes, size_t max...
method tryRead (line 60) | kj::Promise<size_t> tryRead(void* buffer, size_t minBytes, size_t ...
method add (line 73) | void add(QByteArray bytes) {
function consumeFrom (line 97) | kj::Maybe<size_t> consumeFrom(QByteArray& bytes) {
function finishLater (line 114) | kj::Promise<size_t> finishLater() {
function fulfill (line 120) | void fulfill(size_t amount) {
function QString (line 282) | QString EkamDashboardPlugin::findFile(const QString& canonicalPath) {
function foreach (line 297) | foreach (ActionState* action, actions) {
function foreach (line 307) | foreach (ActionState* action, actions) {
function foreach (line 374) | foreach (const ProjectExplorer::Task& task, tasks) {
FILE: qtcreator/ekamdashboardplugin.h
function namespace (line 43) | namespace ProjectExplorer {
function namespace (line 47) | namespace EkamDashboard {
FILE: qtcreator/ekamtreewidget.cpp
type EkamDashboard (line 26) | namespace EkamDashboard {
type Internal (line 27) | namespace Internal {
type StatePrioritiesInitializer (line 47) | struct StatePrioritiesInitializer {
method StatePrioritiesInitializer (line 48) | StatePrioritiesInitializer() {
function QModelIndex (line 80) | QModelIndex EkamTreeNode::index() {
function QVariant (line 143) | QVariant EkamTreeNode::data(int role) {
function foreach (line 247) | foreach (EkamTreeNode* child, childNodes) {
function QModelIndex (line 283) | QModelIndex EkamTreeModel::index(int row, int column, const QModelIn...
function QModelIndex (line 300) | QModelIndex EkamTreeModel::parent(const QModelIndex &index) const {
function QVariant (line 313) | QVariant EkamTreeModel::data(const QModelIndex &index, int role) con...
FILE: qtcreator/ekamtreewidget.h
function namespace (line 30) | namespace EkamDashboard {
FILE: src/base/Debug.cpp
function DebugMessage (line 42) | DebugMessage& DebugMessage::operator<<(const char* value) {
function DebugMessage (line 47) | DebugMessage& DebugMessage::operator<<(const std::string& value) {
FILE: src/base/Debug.h
function namespace (line 22) | namespace ekam {
FILE: src/base/Hash.cpp
type ekam (line 23) | namespace ekam {
function HexDigit (line 27) | char HexDigit(unsigned int value) {
function Hash (line 38) | Hash Hash::of(const std::string& data) {
function Hash (line 42) | Hash Hash::of(void* data, size_t size) {
function Hash (line 73) | Hash Hash::Builder::build() {
FILE: src/base/Hash.h
function namespace (line 26) | namespace ekam {
FILE: src/base/OwnedPtr.cpp
type ekam (line 19) | namespace ekam {
FILE: src/base/OwnedPtr.h
function namespace (line 31) | namespace std { struct nullptr_t; }
function namespace (line 34) | namespace ekam {
function T (line 269) | T* get() const { return ptr; }
function allocate (line 293) | void allocate() {
function reset (line 338) | inline void reset(T* newValue) {
function reset (line 343) | void reset(T* newValue, Refcount* newRefcount) {
function T (line 447) | T* get(int index) const { return vec[index]; }
function add (line 450) | void add(OwnedPtr<T> ptr) {
function set (line 454) | void set(int index, OwnedPtr<T> ptr) {
function clear (line 477) | void clear() {
function swap (line 484) | void swap(OwnedPtrVector* other) {
function class (line 488) | class Appender {
function Appender (line 500) | Appender appender() {
FILE: src/base/Promise.cpp
type ekam (line 19) | namespace ekam {
FILE: src/base/Promise.h
function namespace (line 31) | namespace ekam {
function isException (line 307) | bool isException() {
function Void (line 311) | Void get() {
function Void (line 318) | Void release() {
function namespace (line 342) | namespace promiseInternal {
function propagateCurrentException (line 532) | void propagateCurrentException() {
function propagateCurrentException (line 555) | void propagateCurrentException() {
function namespace (line 569) | namespace promiseInternal {
function void (line 605) | struct CallAndFulfillFunctor<void> {
function dependencyDone (line 643) | void dependencyDone(bool failed) {
type AddDependenciesFunctor (line 664) | struct AddDependenciesFunctor {
function const (line 678) | void operator()(DependentPromiseFulfiller* fulfiller) const {
function addAllDependencies (line 685) | void addAllDependencies() {
type DoReadyFunctor (line 699) | struct DoReadyFunctor {
function ready (line 708) | void ready() {
type DoErrorFunctor (line 727) | struct DoErrorFunctor {
function error (line 738) | void error() {
function readyLater (line 747) | void readyLater() {
function operator (line 801) | bool operator==(std::nullptr_t) {
function operator (line 804) | bool operator!=(std::nullptr_t) {
function explicit (line 819) | explicit Promise(OwnedPtr<State> state): state(state.release()) {
function fulfill (line 835) | inline void PromiseFulfiller<void>::Callback::fulfill(Promise<void> chai...
function namespace (line 839) | namespace promiseInternal {
function Promise (line 916) | inline Promise<void> newFulfilledPromise() {
type ValuePack (line 929) | typedef ValuePack<Types...> ParamPack;
type RESULT_TYPE (line 943) | typedef RESULT_TYPE ResultType;
type RESULT_TYPE (line 949) | typedef RESULT_TYPE ResultType;
type promiseInternal (line 951) | typedef promiseInternal::DependentPromiseFulfiller<
FILE: src/base/Promise_test.cpp
type ekam (line 22) | namespace ekam {
class MockExecutor (line 25) | class MockExecutor: public Executor {
class PendingRunnableImpl (line 27) | class PendingRunnableImpl : public PendingRunnable {
method PendingRunnableImpl (line 29) | PendingRunnableImpl(MockExecutor* executor, OwnedPtr<Runnable> run...
method run (line 43) | void run() {
method MockExecutor (line 53) | MockExecutor() {}
method runNext (line 58) | void runNext() {
method empty (line 65) | bool empty() {
method runLater (line 70) | OwnedPtr<PendingRunnable> runLater(OwnedPtr<Runnable> runnable) {
class MockPromiseFulfiller (line 81) | class MockPromiseFulfiller: public PromiseFulfiller<T> {
method MockPromiseFulfiller (line 84) | MockPromiseFulfiller(Callback* callback, Callback** callbackPtr)
function TEST (line 96) | TEST(PromiseTest, Basic) {
function TEST (line 125) | TEST(PromiseTest, PreFulfilled) {
function TEST (line 146) | TEST(PromiseTest, Dependent) {
function TEST (line 177) | TEST(PromiseTest, Chained) {
function TEST (line 202) | TEST(PromiseTest, ChainedVoid) {
function TEST (line 227) | TEST(PromiseTest, ChainedVoidWhen) {
function TEST (line 264) | TEST(PromiseTest, ChainedPreFulfilled) {
function TEST (line 285) | TEST(PromiseTest, MoveSemantics) {
function TEST (line 305) | TEST(PromiseTest, Cancel) {
function TEST (line 323) | TEST(PromiseTest, VoidPromise) {
function TEST (line 343) | TEST(PromiseTest, Exception) {
function TEST (line 386) | TEST(PromiseTest, ExceptionInCallback) {
function TEST (line 423) | TEST(PromiseTest, ExceptionPropagation) {
FILE: src/base/Table.h
function namespace (line 24) | namespace ekam {
function Row (line 203) | Row* find(const typename Column<columnNumber>::Value& value) const {
function erase (line 215) | size_t erase(const typename Column<columnNumber>::Value& value) {
function add (line 240) | void add(const typename Column<0>::Value& value0,
function has (line 250) | bool has(const typename Column<columnNumber>::Value& value) {
function size (line 263) | int size() {
function capacity (line 266) | int capacity() {
function refresh (line 283) | void refresh() {
type typename (line 312) | typedef typename C::Index::iterator ColumnIterator;
FILE: src/base/Table_test.cpp
type ekam (line 24) | namespace ekam {
function testTable (line 33) | void testTable() {
function main (line 228) | int main(int argc, char* argv[]) {
FILE: src/base/sha256.cpp
function be32enc (line 34) | inline void
function be32dec (line 45) | inline uint32_t
type ekam (line 94) | namespace ekam {
function be32enc_vect (line 112) | static void
function be32dec_vect (line 125) | static void
function SHA256_Transform (line 165) | static void
function SHA256_Pad (line 260) | static void
function SHA256_Init (line 282) | void
function SHA256_Update (line 301) | void
function SHA256_Final (line 347) | void
FILE: src/base/sha256.h
function namespace (line 38) | namespace ekam {
FILE: src/ekam/Action.cpp
type ekam (line 19) | namespace ekam {
FILE: src/ekam/Action.h
function namespace (line 29) | namespace ekam {
FILE: src/ekam/ActionUtil.cpp
type ekam (line 20) | namespace ekam {
FILE: src/ekam/ActionUtil.h
function namespace (line 25) | namespace ekam {
FILE: src/ekam/ConsoleDashboard.cpp
type ekam (line 26) | namespace ekam {
class ConsoleDashboard::LogFormatter (line 28) | class ConsoleDashboard::LogFormatter {
method LogFormatter (line 30) | LogFormatter(const std::string& text)
method atEnd (line 35) | inline bool atEnd() {
method getLine (line 39) | std::string getLine(int startColumn, int windowWidth) {
method eatWhitespace (line 126) | bool eatWhitespace() {
method tryHighlight (line 138) | bool tryHighlight(const char* word, Color color, int windowWidth,
class ConsoleDashboard::TaskImpl (line 176) | class ConsoleDashboard::TaskImpl : public Dashboard::Task {
type winsize (line 274) | struct winsize
type winsize (line 348) | struct winsize
FILE: src/ekam/ConsoleDashboard.h
function namespace (line 24) | namespace ekam {
FILE: src/ekam/CppActionFactory.cpp
type ekam (line 27) | namespace ekam {
function getDepsFile (line 35) | OwnedPtr<File> getDepsFile(File* objectFile) {
function isTestName (line 39) | bool isTestName(const std::string& name) {
class LinkAction (line 51) | class LinkAction : public Action {
type Mode (line 53) | enum Mode {
class DepsSet (line 69) | class DepsSet {
method DepsSet (line 71) | DepsSet() {}
method enumerate (line 75) | void enumerate(OwnedPtrVector<File>::Appender output) {
FILE: src/ekam/CppActionFactory.h
function namespace (line 24) | namespace ekam {
FILE: src/ekam/Dashboard.cpp
type ekam (line 19) | namespace ekam {
FILE: src/ekam/Dashboard.h
function namespace (line 23) | namespace ekam {
FILE: src/ekam/Driver.cpp
type ekam (line 29) | namespace ekam {
function fileDepth (line 33) | int fileDepth(const std::string& name) {
function commonPrefixLength (line 43) | int commonPrefixLength(const std::string& srcName, const std::string& ...
class Driver::ActionDriver (line 55) | class Driver::ActionDriver : public BuildContext, public EventGroup::E...
type Installation (line 110) | struct Installation {
function File (line 165) | File* Driver::ActionDriver::findProvider(Tag tag) {
function File (line 178) | File* Driver::ActionDriver::findInput(const std::string& path) {
function File (line 188) | File* Driver::ActionDriver::provideInternal(File* file, const std::vec...
class Deferred (line 328) | class Deferred {
method Deferred (line 330) | inline Deferred(Func&& func): func(func), canceled(false) {}
method Deferred (line 334) | inline Deferred(Deferred&& other): func(other.func), canceled(false) {
function defer (line 343) | Deferred<Func> defer(Func&& func) {
FILE: src/ekam/Driver.h
function namespace (line 32) | namespace ekam {
FILE: src/ekam/ExecPluginActionFactory.cpp
type ekam (line 27) | namespace ekam {
function splitToken (line 31) | std::string splitToken(std::string* line) {
class PluginDerivedActionFactory (line 48) | class PluginDerivedActionFactory : public ActionFactory {
class PluginDerivedAction (line 69) | class PluginDerivedAction : public Action {
method PluginDerivedAction (line 71) | PluginDerivedAction(File* executable, const std::string& verb, bool ...
method getVerb (line 80) | std::string getVerb() { return verb; }
method isSilent (line 81) | bool isSilent() { return silent; }
class CommandReader (line 85) | class CommandReader
class PluginDerivedAction::CommandReader (line 93) | class PluginDerivedAction::CommandReader {
method CommandReader (line 95) | CommandReader(BuildContext* context, OwnedPtr<ByteStream> requestStr...
method readAll (line 111) | Promise<void> readAll(EventManager* eventManager) {
method consume (line 137) | void consume(const std::string& line) {
method eof (line 270) | void eof() {
method findInCache (line 313) | bool findInCache(const std::string& line) {
FILE: src/ekam/ExecPluginActionFactory.h
function namespace (line 22) | namespace ekam {
FILE: src/ekam/MuxDashboard.cpp
type ekam (line 23) | namespace ekam {
class MuxDashboard::TaskImpl (line 25) | class MuxDashboard::TaskImpl : public Dashboard::Task {
FILE: src/ekam/MuxDashboard.h
function namespace (line 24) | namespace ekam {
FILE: src/ekam/ProtoDashboard.cpp
type ekam (line 29) | namespace ekam {
class ProtoDashboard::TaskImpl (line 31) | class ProtoDashboard::TaskImpl : public Dashboard::Task {
class NetworkAcceptingDashboard (line 187) | class NetworkAcceptingDashboard : public Dashboard {
method NetworkAcceptingDashboard (line 189) | NetworkAcceptingDashboard(EventManager* eventManager, const std::str...
method doAccept (line 198) | Promise<void> doAccept() {
method beginTask (line 209) | OwnedPtr<Task> beginTask(const std::string& verb, const std::string&...
class ConnectedProtoDashboard (line 221) | class ConnectedProtoDashboard {
method ConnectedProtoDashboard (line 223) | ConnectedProtoDashboard(NetworkAcceptingDashboard* owner, EventMan...
function initNetworkDashboard (line 249) | OwnedPtr<Dashboard> initNetworkDashboard(EventManager* eventManager, c...
FILE: src/ekam/ProtoDashboard.h
function namespace (line 28) | namespace ekam {
FILE: src/ekam/SimpleDashboard.cpp
type ekam (line 19) | namespace ekam {
class SimpleDashboard::TaskImpl (line 21) | class SimpleDashboard::TaskImpl : public Dashboard::Task {
FILE: src/ekam/SimpleDashboard.h
function namespace (line 24) | namespace ekam {
FILE: src/ekam/Tag.cpp
type ekam (line 20) | namespace ekam {
function canonicalizePath (line 24) | std::string canonicalizePath(const std::string& path) {
function Tag (line 69) | Tag Tag::fromFile(const std::string& path) {
FILE: src/ekam/Tag.h
function namespace (line 27) | namespace ekam {
FILE: src/ekam/ekam-client.cpp
type ekam (line 27) | namespace ekam {
function dump (line 29) | void dump(proto::TaskUpdate::Reader message) {
function toDashboardState (line 62) | Dashboard::TaskState toDashboardState(proto::TaskUpdate::State state) {
function main (line 81) | int main(int argc, char* argv[]) {
function main (line 156) | int main(int argc, char* argv[]) {
FILE: src/ekam/ekam.cpp
type ekam (line 41) | namespace ekam {
class ExtractTypeAction (line 43) | class ExtractTypeAction : public Action {
method ExtractTypeAction (line 45) | ExtractTypeAction(File* file) : file(file->clone()) {}
method isSilent (line 49) | bool isSilent() { return true; }
method getVerb (line 50) | std::string getVerb() { return "scan"; }
method start (line 52) | Promise<void> start(EventManager* eventManager, BuildContext* contex...
class ExtractTypeActionFactory (line 87) | class ExtractTypeActionFactory : public ActionFactory {
method ExtractTypeActionFactory (line 89) | ExtractTypeActionFactory() {}
method enumerateTriggerTags (line 93) | void enumerateTriggerTags(std::back_insert_iterator<std::vector<Tag>...
method tryMakeAction (line 96) | OwnedPtr<Action> tryMakeAction(const Tag& id, File* file) {
function usage (line 101) | void usage(const char* command, FILE* out) {
class Watcher (line 130) | class Watcher {
method Watcher (line 132) | Watcher(OwnedPtr<File> file, EventManager* eventManager, Driver* dri...
method resetWatch (line 145) | void resetWatch() {
method waitForEvent (line 152) | void waitForEvent() {
method clearWatch (line 167) | void clearWatch() {
method isDeleted (line 171) | bool isDeleted() {
class FileWatcher (line 186) | class FileWatcher : public Watcher {
method FileWatcher (line 188) | FileWatcher(OwnedPtr<File> file, EventManager* eventManager, Driver*...
method created (line 193) | void created() {
method modified (line 197) | void modified() {
method deleted (line 202) | void deleted() {
method reallyDeleted (line 214) | void reallyDeleted() {
class DirectoryWatcher (line 222) | class DirectoryWatcher : public Watcher {
method DirectoryWatcher (line 225) | DirectoryWatcher(OwnedPtr<File> file, EventManager* eventManager, Dr...
method created (line 230) | void created() {
method modified (line 234) | void modified() {
method deleted (line 303) | void deleted() {
method reallyDeleted (line 315) | void reallyDeleted() {
class EkamLocks (line 336) | class EkamLocks final: public Driver::ActivityObserver {
method EkamLocks (line 338) | EkamLocks(File* tmp)
method tryTakeMainLock (line 344) | bool tryTakeMainLock() {
method waitForOther (line 361) | void waitForOther() {
method hasFailures (line 377) | bool hasFailures() {
method startingAction (line 382) | void startingAction() override {
method idle (line 389) | void idle(bool hasFailures) override {
method openLockfile (line 405) | static int openLockfile(File* lockfile) {
function scanSourceTree (line 414) | void scanSourceTree(File* src, Driver* driver) {
function getDashboard (line 436) | OwnedPtr<Dashboard> getDashboard(int maxDisplayedLogLines) {
function main (line 464) | int main(int argc, char* argv[]) {
function main (line 604) | int main(int argc, char* argv[]) {
FILE: src/ekam/initNetworkDashboardStub.cpp
type ekam (line 20) | namespace ekam {
function initNetworkDashboard (line 22) | OwnedPtr<Dashboard> initNetworkDashboard(EventManager* eventManager, c...
FILE: src/ekam/rules/intercept.c
function start_interceptor (line 47) | void __attribute__((constructor)) start_interceptor() {
function fake_pthread_once (line 102) | int fake_pthread_once(pthread_once_t* once_control, void (*init_func)(vo...
function fake_pthread_mutex_lock (line 133) | int fake_pthread_mutex_lock(pthread_mutex_t* mutex) { return 0; }
function fake_pthread_mutex_unlock (line 134) | int fake_pthread_mutex_unlock(pthread_mutex_t* mutex) { return 0; }
function init_pthreads (line 136) | void init_pthreads() {
function init_bypass_interceptor (line 183) | static void init_bypass_interceptor() {
function init_streams_once (line 217) | static void init_streams_once() {
function init_streams (line 256) | static void init_streams() {
type usage_t (line 265) | typedef enum usage {
function cache_result (line 282) | static void cache_result(const char* input, const char* output, usage_t ...
function get_cached_result (line 290) | static int get_cached_result(const char* pathname, char* buffer, usage_t...
function canonicalizePath (line 301) | static void canonicalizePath(char* path) {
function path_has_prefix (line 380) | static bool path_has_prefix(const char* pathname, const char* prefix, si...
function bypass_remap (line 393) | static bool bypass_remap(const char *pathname) {
function is_temporary_dir (line 439) | static bool is_temporary_dir(const char *pathname) {
function stat_inode64 (line 683) | int stat_inode64(const char* path, void* sb) {
function direct_stat (line 707) | static int direct_stat(const char* path, struct stat* sb) {
type stat (line 731) | struct stat
function __lxstat (line 732) | int __lxstat (int ver, const char* path, struct stat* sb) {
function ___lxstat (line 754) | int ___lxstat (int ver, const char* path, struct stat* sb) {
type stat64 (line 758) | struct stat64
function __lxstat64 (line 759) | int __lxstat64 (int ver, const char* path, struct stat64* sb) {
function ___lxstat64 (line 781) | int ___lxstat64 (int ver, const char* path, struct stat64* sb) {
type stat (line 794) | struct stat
function lstat (line 795) | int lstat(const char* path, struct stat* sb) {
function _lstat (line 817) | int _lstat(const char* path, struct stat* sb) {
function execvpe (line 843) | int execvpe(const char* path, char* const argv[], char* const envp[]) {
function _execvpe (line 861) | int _execvpe (const char* path, char* const argv[], char* const envp[]) {
function execvp (line 866) | int execvp(const char* path, char* const argv[]) {
function _execvp (line 884) | int _execvp (const char* path, char* const argv[]) {
function direct_stat (line 889) | static int direct_stat(const char* path, struct stat* sb) {
function readlink (line 914) | ssize_t readlink(const char *path, char *buf, size_t bufsiz) {
function direct_stat (line 929) | static int direct_stat(const char* path, struct stat* sb) {
function intercepted_open (line 942) | static int intercepted_open(const char * pathname, int flags, va_list ar...
function open (line 989) | int open(const char * pathname, int flags, ...) {
function _open (line 995) | int _open(const char * pathname, int flags, ...) {
function intercepted_open64 (line 1001) | static int intercepted_open64(const char * pathname, int flags, va_list ...
function open64 (line 1022) | int open64(const char * pathname, int flags, ...) {
function _open64 (line 1028) | int _open64(const char * pathname, int flags, ...) {
function intercepted_openat (line 1034) | static int intercepted_openat(int dirfd, const char * pathname, int flag...
function openat (line 1066) | int openat(int dirfd, const char * pathname, int flags, ...) {
function _openat (line 1072) | int _openat(int dirfd, const char * pathname, int flags, ...) {
function intercepted_openat64 (line 1078) | static int intercepted_openat64(int dirfd, const char * pathname, int fl...
function openat64 (line 1110) | int openat64(int dirfd, const char * pathname, int flags, ...) {
function _openat64 (line 1116) | int _openat64(int dirfd, const char * pathname, int flags, ...) {
function rename (line 1124) | int rename(const char* from, const char* to) {
function _rename (line 1140) | int _rename(const char* from, const char* to) {
function mkdir (line 1147) | int mkdir(const char* path, mode_t mode) {
function _mkdir (line 1186) | int _mkdir(const char* path, mode_t mode) {
function access (line 1195) | int access(const char* path, int mode) {
FILE: src/os/ByteStream.cpp
type ekam (line 28) | namespace ekam {
type stat (line 69) | struct stat
FILE: src/os/ByteStream.h
function namespace (line 28) | namespace ekam {
FILE: src/os/DiskFile.cpp
type ekam (line 35) | namespace ekam {
class DirectoryReader (line 41) | class DirectoryReader {
method DirectoryReader (line 43) | DirectoryReader(const std::string& path)
method next (line 58) | bool next(std::string* output) {
function statIfExists (line 78) | bool statIfExists(const std::string& path, struct stat* output) {
class DiskFile::DiskRefImpl (line 155) | class DiskFile::DiskRefImpl : public File::DiskRef {
method DiskRefImpl (line 157) | DiskRefImpl(const std::string& path) : pathName(path) {}
type stat (line 172) | struct stat
type stat (line 177) | struct stat
type stat (line 182) | struct stat
function Hash (line 187) | Hash DiskFile::contentHash() {
type stat (line 214) | struct stat
FILE: src/os/DiskFile.h
function namespace (line 23) | namespace ekam {
FILE: src/os/EpollEventManager.cpp
type ekam (line 40) | namespace ekam {
function epollEventsToString (line 44) | std::string epollEventsToString(uint32_t events) {
function statIfExists (line 83) | bool statIfExists(const std::string& path, struct stat* output) {
function isDirectory (line 98) | bool isDirectory(const std::string& path) {
type epoll_event (line 184) | struct epoll_event
type epoll_event (line 203) | struct epoll_event
function sigset_t (line 223) | sigset_t getHandledSignals() {
type signalfd_siginfo (line 248) | struct signalfd_siginfo
class EpollEventManager::AsyncCallbackHandler (line 273) | class EpollEventManager::AsyncCallbackHandler : public PendingRunnable {
method AsyncCallbackHandler (line 275) | AsyncCallbackHandler(EpollEventManager* eventManager, OwnedPtr<Runna...
method run (line 292) | void run() {
class EpollEventManager::SignalHandler::ProcessExitHandler (line 309) | class EpollEventManager::SignalHandler::ProcessExitHandler
method ProcessExitHandler (line 312) | ProcessExitHandler(Callback* callback, SignalHandler* signalHandler,...
method handle (line 327) | void handle(int waitStatus) {
class EpollEventManager::IoWatcherImpl (line 398) | class EpollEventManager::IoWatcherImpl: public IoWatcher, public IoHan...
method IoWatcherImpl (line 400) | IoWatcherImpl(Epoller* epoller, int fd)
method onReadable (line 415) | Promise<void> onReadable() {
method onWritable (line 422) | Promise<void> onWritable() {
method handle (line 430) | void handle(uint32_t events) {
class Fulfiller (line 444) | class Fulfiller: public PromiseFulfiller<void> {
method Fulfiller (line 446) | Fulfiller(Callback* callback, Epoller::Watch* watch, uint32_t even...
method ready (line 458) | void ready() {
method abandon (line 465) | void abandon() {
class EpollEventManager::InotifyHandler::WatchedDirectory (line 494) | class EpollEventManager::InotifyHandler::WatchedDirectory {
class CallbackTable (line 495) | class CallbackTable : public Table<IndexedColumn<std::string>,
method WatchedDirectory (line 503) | WatchedDirectory(InotifyHandler* inotifyHandler, const std::string& ...
method addWatch (line 532) | void addWatch(const std::string& basename, FileWatcherImpl* op) {
method removeWatch (line 537) | void removeWatch(FileWatcherImpl* op) {
method invalidate (line 545) | void invalidate() {
type inotify_event (line 553) | struct inotify_event
method deleteSelfIfEmpty (line 562) | void deleteSelfIfEmpty() {
method basenameForOp (line 572) | std::string basenameForOp(FileWatcherImpl* op) {
class EpollEventManager::InotifyHandler::FileWatcherImpl (line 582) | class EpollEventManager::InotifyHandler::FileWatcherImpl: public FileW...
method FileWatcherImpl (line 584) | FileWatcherImpl(InotifyHandler* inotifyHandler, const std::string& f...
method flagAsModified (line 625) | void flagAsModified() {
method flagAsDeleted (line 630) | void flagAsDeleted() {
method onChange (line 636) | Promise<FileChangeType> onChange() {
class Fulfiller (line 646) | class Fulfiller: public PromiseFulfiller<FileChangeType> {
method Fulfiller (line 648) | Fulfiller(Callback* callback, Fulfiller** ptr)
method fulfill (line 658) | void fulfill(FileChangeType type) {
method abandon (line 664) | void abandon() {
method maybeFulfill (line 684) | void maybeFulfill() {
type inotify_event (line 703) | struct inotify_event
type inotify_event (line 761) | struct inotify_event
type inotify_event (line 785) | struct inotify_event
type inotify_event (line 790) | struct inotify_event
type inotify_event (line 790) | struct inotify_event
type inotify_event (line 792) | struct inotify_event
type inotify_event (line 798) | struct inotify_event
function newPreferredEventManager (line 857) | OwnedPtr<RunnableEventManager> newPreferredEventManager() {
FILE: src/os/EpollEventManager.h
type PollFd (line 33) | typedef struct pollfd PollFd;
function namespace (line 35) | namespace ekam {
FILE: src/os/EventGroup.cpp
type ekam (line 21) | namespace ekam {
class EventGroup::PendingEvent (line 25) | class EventGroup::PendingEvent {
method PendingEvent (line 27) | PendingEvent(EventGroup* group): group(group) {
class EventGroup::RunnableWrapper (line 51) | class EventGroup::RunnableWrapper : public Runnable {
method RunnableWrapper (line 53) | RunnableWrapper(EventGroup* group, OwnedPtr<Runnable> wrapped)
method run (line 58) | void run() { HANDLE_EXCEPTIONS(wrapped->run()); }
class EventGroup::IoWatcherWrapper (line 88) | class EventGroup::IoWatcherWrapper: public EventManager::IoWatcher {
method IoWatcherWrapper (line 90) | IoWatcherWrapper(EventGroup* group, OwnedPtr<IoWatcher> inner)
method onReadable (line 95) | Promise<void> onReadable() {
method onWritable (line 102) | Promise<void> onWritable() {
class EventGroup::FileWatcherWrapper (line 119) | class EventGroup::FileWatcherWrapper: public EventManager::FileWatcher {
method FileWatcherWrapper (line 121) | FileWatcherWrapper(EventGroup* group, OwnedPtr<FileWatcher> inner)
method onChange (line 126) | Promise<FileChangeType> onChange() {
FILE: src/os/EventGroup.h
function namespace (line 24) | namespace ekam {
FILE: src/os/EventManager.cpp
type ekam (line 23) | namespace ekam {
FILE: src/os/EventManager.h
function class (line 28) | class ProcessExitCode {
function class (line 62) | class EventManager : public Executor {
FILE: src/os/File.cpp
type ekam (line 20) | namespace ekam {
function splitExtension (line 25) | void splitExtension(const std::string& name, std::string* base, std::s...
function recursivelyCreateDirectory (line 37) | void recursivelyCreateDirectory(File* location) {
FILE: src/os/File.h
function namespace (line 25) | namespace ekam {
FILE: src/os/KqueueEventManager.h
type KEvent (line 27) | typedef struct kevent KEvent;
function namespace (line 29) | namespace ekam {
FILE: src/os/OsHandle.cpp
type ekam (line 29) | namespace ekam {
function toString (line 45) | std::string toString(const char* arg) {
function toString (line 48) | std::string toString(int arg) {
function toString (line 53) | std::string toString(const OsHandle& arg) {
FILE: src/os/OsHandle.h
function namespace (line 26) | namespace ekam {
FILE: src/os/PollEventManager.h
type PollFd (line 29) | typedef struct pollfd PollFd;
function namespace (line 31) | namespace ekam {
FILE: src/os/Socket.cpp
type ekam (line 30) | namespace ekam {
function splitFirst (line 34) | std::string splitFirst(std::string* str, char delim) {
function parseIpAddr (line 47) | bool parseIpAddr(std::string text, struct sockaddr_in* addr) {
type sockaddr_in (line 98) | struct sockaddr_in
type sockaddr (line 103) | struct sockaddr
FILE: src/os/Socket.h
function namespace (line 25) | namespace ekam {
FILE: src/os/Subprocess.cpp
type ekam (line 31) | namespace ekam {
FILE: src/os/Subprocess.h
function namespace (line 28) | namespace ekam {
FILE: vscode/src/extension.ts
function getConfig (line 9) | function getConfig<T>(option: string, defaultValue?: any): T {
function activate (line 18) | function activate(context: vscode.ExtensionContext) {
Condensed preview — 92 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (505K chars).
[
{
"path": ".gitignore",
"chars": 48,
"preview": "tmp\neclipse/bin\n.cproject\n.project\nbin\nlib\ndeps\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 1116,
"preview": "## Contributing to Ekam\n\n### Building\n\nUse `make continuous` to build Ekam continuously and watch for changes. For LSP s"
},
{
"path": "CONTRIBUTORS",
"chars": 431,
"preview": "The following people have made large code contributions to this repository.\nThose contributions are copyright the respec"
},
{
"path": "LICENSE",
"chars": 10779,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 2762,
"preview": "# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Google Inc., an"
},
{
"path": "README.md",
"chars": 23538,
"preview": "# Ekam Build System\n\nEkam (\"make\" backwards) is a build system which automatically figures out what to build and how to "
},
{
"path": "qtcreator/COPYING.txt",
"chars": 441,
"preview": "Code in this directory is licensed under Apache 2.0 like normal.\n\nHowever, the icons under images/ are from the Qt Creat"
},
{
"path": "qtcreator/EkamDashboard.json.in",
"chars": 419,
"preview": "{\n \\\"Name\\\" : \\\"EkamDashboard\\\",\n \\\"Version\\\" : \\\"0.0.1\\\",\n \\\"CompatVersion\\\" : \\\"0.0.1\\\",\n \\\"Vendor\\\" : \\\"KentonVar"
},
{
"path": "qtcreator/EkamDashboard.pluginspec.in",
"chars": 871,
"preview": "<plugin name=\\\"EkamDashboard\\\" version=\\\"0.0.1\\\" compatVersion=\\\"0.0.1\\\">\n <vendor>KentonVarda</vendor>\n <copyrigh"
},
{
"path": "qtcreator/ekamdashboard.pro",
"chars": 3226,
"preview": "# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Google Inc., an"
},
{
"path": "qtcreator/ekamdashboard.qrc",
"chars": 726,
"preview": "<RCC>\n <qresource prefix=\"/ekamdashboard\">\n <file>images/state-blocked.png</file>\n <file>images/state-d"
},
{
"path": "qtcreator/ekamdashboard_global.h",
"chars": 963,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "qtcreator/ekamdashboardconstants.h",
"chars": 1050,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "qtcreator/ekamdashboardplugin.cpp",
"chars": 14365,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "qtcreator/ekamdashboardplugin.h",
"chars": 3978,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "qtcreator/ekamtreewidget.cpp",
"chars": 13300,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "qtcreator/ekamtreewidget.h",
"chars": 3682,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Debug.cpp",
"chars": 2256,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Debug.h",
"chars": 2538,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Hash.cpp",
"chars": 1932,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Hash.h",
"chars": 2172,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/OwnedPtr.cpp",
"chars": 756,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/OwnedPtr.h",
"chars": 17790,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Promise.cpp",
"chars": 861,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Promise.h",
"chars": 25224,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Promise_test.cpp",
"chars": 11799,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Table.h",
"chars": 9930,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/Table_test.cpp",
"chars": 5953,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/base/sha256.cpp",
"chars": 10852,
"preview": "/********************************************************************\n * Pieces of endian.h from FreeBSD "
},
{
"path": "src/base/sha256.h",
"chars": 2303,
"preview": "/********************************************************************\n * sha256.h from FreeBSD's libmd "
},
{
"path": "src/ekam/Action.cpp",
"chars": 1026,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/Action.h",
"chars": 2380,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ActionUtil.cpp",
"chars": 2720,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ActionUtil.h",
"chars": 1419,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ConsoleDashboard.cpp",
"chars": 11475,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ConsoleDashboard.h",
"chars": 2082,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/CppActionFactory.cpp",
"chars": 10164,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/CppActionFactory.h",
"chars": 1448,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/Dashboard.cpp",
"chars": 794,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/Dashboard.h",
"chars": 1686,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/Driver.cpp",
"chars": 26813,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/Driver.h",
"chars": 4059,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ExecPluginActionFactory.cpp",
"chars": 13407,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ExecPluginActionFactory.h",
"chars": 1274,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/MuxDashboard.cpp",
"chars": 4649,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/MuxDashboard.h",
"chars": 1478,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ProtoDashboard.cpp",
"chars": 8890,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ProtoDashboard.h",
"chars": 2318,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/SimpleDashboard.cpp",
"chars": 3194,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/SimpleDashboard.h",
"chars": 1268,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/Tag.cpp",
"chars": 1885,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/Tag.h",
"chars": 2119,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/dashboard.capnp",
"chars": 1256,
"preview": "# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Google Inc., an"
},
{
"path": "src/ekam/ekam-client.cpp",
"chars": 5114,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ekam-langserve.c++",
"chars": 22677,
"preview": "#include <ekam/langserve.capnp.h>\n#include <ekam/dashboard.capnp.h>\n#include <kj/main.h>\n#include <capnp/compat/json-rpc"
},
{
"path": "src/ekam/ekam.cpp",
"chars": 18503,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/ekam.ekam-manifest",
"chars": 44,
"preview": "ekam bin\nekam-client bin\nekam-langserve bin\n"
},
{
"path": "src/ekam/initNetworkDashboardStub.cpp",
"chars": 1030,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/langserve.capnp",
"chars": 2871,
"preview": "@0xdf85b05eff43f858;\n# Cap'n Proto definitions for a subset of the Language Server Protocol, usable with capnp::JsonRpc."
},
{
"path": "src/ekam/rules/compile.ekam-rule",
"chars": 7674,
"preview": "#! /bin/sh\n\n# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Goo"
},
{
"path": "src/ekam/rules/include.ekam-rule",
"chars": 1400,
"preview": "#! /bin/sh\n\n# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Goo"
},
{
"path": "src/ekam/rules/install.ekam-rule",
"chars": 1319,
"preview": "#! /bin/sh\n\n# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Goo"
},
{
"path": "src/ekam/rules/intercept.c",
"chars": 41580,
"preview": "/* Ekam Build System\n * Author: Kenton Varda (kenton@sandstorm.io)\n * Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/ekam/rules/intercept.ekam-rule",
"chars": 1342,
"preview": "#! /bin/sh\n\n# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Goo"
},
{
"path": "src/ekam/rules/proto.ekam-rule",
"chars": 2375,
"preview": "#! /bin/sh\n\n# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Goo"
},
{
"path": "src/ekam/rules/test.ekam-rule",
"chars": 1735,
"preview": "#! /bin/sh\n\n# Ekam Build System\n# Author: Kenton Varda (kenton@sandstorm.io)\n# Copyright (c) 2010-2015 Kenton Varda, Goo"
},
{
"path": "src/os/ByteStream.cpp",
"chars": 3325,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/ByteStream.h",
"chars": 1973,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/DiskFile.cpp",
"chars": 8486,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/DiskFile.h",
"chars": 1841,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/EpollEventManager.cpp",
"chars": 27901,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/EpollEventManager.h",
"chars": 4156,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/EventGroup.cpp",
"chars": 5153,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/EventGroup.h",
"chars": 2555,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/EventManager.cpp",
"chars": 1307,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/EventManager.h",
"chars": 2757,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/File.cpp",
"chars": 1425,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/File.h",
"chars": 2402,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/KqueueEventManager.h",
"chars": 2390,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/OsHandle.cpp",
"chars": 2359,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/OsHandle.h",
"chars": 2691,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/PollEventManager.h",
"chars": 2192,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/Socket.cpp",
"chars": 3931,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/Socket.h",
"chars": 1248,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/Subprocess.cpp",
"chars": 4942,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "src/os/Subprocess.h",
"chars": 1686,
"preview": "// Ekam Build System\n// Author: Kenton Varda (kenton@sandstorm.io)\n// Copyright (c) 2010-2015 Kenton Varda, Google Inc.,"
},
{
"path": "vscode/.gitignore",
"chars": 42,
"preview": "out\nnode_modules\npackage-lock.json\n*.vsix\n"
},
{
"path": "vscode/LICENSE",
"chars": 1079,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2018 Kenton Varda\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "vscode/README.md",
"chars": 793,
"preview": "# Ekam VS Code Plugin\n\nBrings error reports from Ekam into Visual Studio Code.\n\n## Usage\n\nThe extension will look for `e"
},
{
"path": "vscode/package.json",
"chars": 1732,
"preview": "{\n \"name\": \"vscode-ekam\",\n \"displayName\": \"vscode-ekam\",\n \"description\": \"Ekam Language Server\",\n \"version\":"
},
{
"path": "vscode/src/extension.ts",
"chars": 1299,
"preview": "import * as vscode from 'vscode';\nimport * as vscodelc from 'vscode-languageclient';\n\n/**\n * Method to get workspace con"
},
{
"path": "vscode/tsconfig.json",
"chars": 702,
"preview": "{\n \"compilerOptions\": {\n \"module\": \"commonjs\",\n \"target\": \"es6\",\n \"outDir\": \"out\",\n \"lib\""
}
]
About this extraction
This page contains the full source code of the sandstorm-io/ekam GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 92 files (470.6 KB), approximately 122.9k tokens, and a symbol index with 407 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.