Skip to content

Instantly share code, notes, and snippets.

@halfcoder
Last active August 29, 2015 14:18
Show Gist options
  • Save halfcoder/d0960c1405e0b4d71493 to your computer and use it in GitHub Desktop.
Save halfcoder/d0960c1405e0b4d71493 to your computer and use it in GitHub Desktop.
sandstorm-io/ekam 项目的 README.md 的中文翻译

# Ekam Build System

Ekam 构建系统

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.

Ekam (“make”的逆序)是一个纯粹基于源代码自动推断构建目标和构建方法的构建系统。

No separate "makefile" is needed.

单独编写的 Makefile 文件已经不再需要了。

Ekam works by exploration.

Ekam 通过探索源代码来工作。

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).

例如,当它遇到一个扩展名为 .cpp 的文件时,它会尝试编译文件,并拦截系统调用以发现它的依赖项(比如包含的头文件)。

If some of these are missing, Ekam continues to explore until it finds headers matching them.

如果其中一些暂时找不到, Ekam 会继续探索直到它发现匹配的头文件。

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.

当 Ekam 构建目标文件并且发现它包含一个 main 符号时,它会尝试链接文件,为此搜索其他目标文件以满足其中引用的所有符号。

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发现测试代码时,它会运行测试,并在此拦截系统调用以动态发现测试需要的输入(这个特性可能尚未实现)。

Ekam can be extended to understand new file types by writing simple shell scripts telling it what to do with them.

Ekam可以通过写简单的shell script程序延伸了解新的文件类型告诉它什么做与他们。

Thus Ekam is, in fact, Make in reverse.

所以Ekam实际上是。

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.

Make 从读取 Makefile 文件开始,查看需要构建的可执行文件,然后从那里推测需要被编译和链接的源文件,之后进行编译。

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 从查看要编译的源文件开始,然后确定由此可以构建的可执行文件,并在最后,可能会输出一个 Makefile 文件描述它刚刚做了什么。

Ekam is a work in progress.

Ekam 正在逐步发展中。

## Building Ekam

构建 Ekam

### Warning

警告

Ekam is an experimental project that is not ready for wide use.

Ekam 是一个实验性项目,尚未适合广泛使用。

That said, I (Kenton) have successfully used Ekam as my primary build system throughout the development of Cap'n Proto and Sandstorm.

但即便如此, 我(Kenton) 也已经成功地在 Cap'n ProtoSandstorm的开发中使用 Ekam 作为我主要的构建系统了。

### Supported Platforms

支持的平台

At this time, Ekam only runs on Linux.

目前, Ekam 只能运行在 Linux 上。

It requires GCC 4.8+ or Clang 3.3+, as it uses C++11 features.

因为它使用了 C++11 的特性,而这需要 GCC 4.8+ 或 Clang 3.3+。

In the past, Ekam worked on FreeBSD and Max OSX, but the code to support that atrophied and was eventually deleted.

之前, Ekam 工作在 FreeBSD和 Mac OS X 上,但支持它们的代码越来越少并且最后完全删除了。

Ekam uses a lot of OS-specific hacks and so is unlikely to work on any platform which is not explicitly supported.

Ekam 使用很多(Linux)系统特有的“黑科技”,因此不太可能在任何非显式支持的平台上工作。

We'd like to see other platforms supported, but Ekam's primary user and maintainer right now (Sandstorm.io) is itself highly Linux-specific, so there is not much pressure.

我们希望看到其他平台也能被支持,但 Ekam 当前的主要用户和维护者(Sandstorm.io) 本身就是十分特定于Linux的,所以暂时没有兼容其他平台的压力。

(Let us know if you want to help.)

(如果您想帮助我们,请告诉我们。)

### Bootstrapping the build

开始构建

Download and compile Ekam like so:

下载并编译 Ekam ,就像这样:

git clone https://github.com/sandstorm-io/ekam.git 
cd ekam
make

If successful, Ekam should have built itself, with the output binary at "bin/ekam".

如果成功的话, Ekam 应该完成对于自身的构建,并输出二进制文件到 bin/ekam

Yes, we use make in order bootstrap Ekam, mostly just because it's slighly nicer than a shell script.

是的,我们使用 make 来构建 Ekam ,主要是因为它比 Shell 脚本更简洁。

### Compiling Ekam with Ekam

使用 Ekam 编译 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.

编译 Ekam 需要设置 GCC 标志 -std=gnu++0x 以启用 C++11 特性,不过现在还没有办法由代码指定所需的编译器标志。

You can only specify them via environment variable.

你只能通过环境变量指定他们。

So, to build Ekam with Ekam, type this command at the top of the Ekam repository:

所以,为了使用 Ekam 构建 Ekam,在 Ekam 版本库的顶层目录下输入如下命令:

CXXFLAGS=-std=gnu++0x ekam -j4

CXXFLAGS=-std=gnu++0x ekam -j4

The -j4 tells Ekam to run up to four tasks at once.

-j4 标志告诉 Ekam 一次最多进行四个任务。

You may want to adjust this number depending on how many CPU cores you have.

你或许想要根据你的 CPU 核心数来调整这个数值。

Note that Ekam looks for a directory called src within the current directory, and scans it for source code.

注意 Ekam 会在当前目录中查找 src 目录,并遍历它以获取源代码。

The Ekam source repository is already set up with such a src subdirectory containing the Ekam code.

Ekam 的源代码版本库本就已经有这么一个名为 src 的,包含了 Ekam 的代码的子目录了。

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.

但你也可以把整个 Ekam 版本库放到其它的名叫 src 的目录中,然后从其上层目录中运行 Ekam ,它也能找到代码。

The Protocol Buffers instructions below will take advantage of this to create a directory tree containing both Ekam and protobufs.

下面关于 Protocol Buffers 的说明会利用这一特性来创建一棵同时包含 Ekam 和 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.

Ekam 将输出存放在 src 的同级目录 tmp (对于临时文件)、bin (对于输出的二进制文件)和 lib (对于输出的库,虽然现在 Ekam 尚不支持构建库),等等。

These are intended to model Unix directory tree conventions.

这些是为了仿照 Unix 目录树结构的惯例。

## Continuous Building

持续构建

If you invoke Ekam with the -c option, it will watch the source tree for changes and rebuild derived files as needed.

如果你使用 -c 选项来启动 Ekam,它将监测源码树的改变并在需要的时候重新编译衍生文件。

In this way, you can simply leave Ekam running while you work on your code, and get information about errors almost immediately on saving.

这样,你就可以一边运行 Ekam 一边编写代码,并且在保存时立刻获得编译出错的信息。

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.

注意持续构建是使用 Ekam 进行增量编译的唯一方法——一旦你运行一个新的 Ekam 进程,它总是会从头开始。

I generally just leave Ekam running in a console window 24/7.

我一般会让 Ekam 7x24小时都在一个命令行窗口中运行着。

## IDE plugins and other external clients

IDE插件以及其它外部客户端

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 能给在运行的同时对外提供一个网络接口,使其它程序能够查询构建状态,包括获得任务树和错误日志。

### Ekam Server

Ekam 服务器

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.

如果你将 -n 标志传递给 Ekam,它会在一个端口上监听连接并将构建状态的更新传递到任何连接了这个端口的程序。

Invoke like:

像下面这样启动它:

ekam -n :41315 

### Ekam Client

Ekam 客户端

ekam-client is a very simple program that prints an Ekam build status stream to the console exactly as Ekam itself does.

ekam-client 是一个非常简单的程序,能向控制台逐步输出 Ekam 的构建状态,就像 Ekam 那样。

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:

其实现在 ekam-client 并不知道如何创建网络连接而是从标准输入中读取输入流,所以你可以像这样启动它:

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.

ekam-client 目前仅仅是个技术演示版本,它显示的信息和在 Ekam 自身运行的控制台能到见的一样。

### Qt Creator Plugin

Qt Creator 插件

The qtcreator directory in the Ekam repository contains source code for a Qt Creator plugin.

Ekam 版本库中的 qtcreator 目录包含了一个 Qt Creator 插件的源代码。

Qt Creator is an open source C++ IDE that, despite its name, is quite nice even for non-Qt code.

Qt Creator 是一个开源 C++ IDE, 虽然它以“Qt”命名,但用于非Qt代码也是极好的。

To build the Qt Creator plugin, you'll want to download the Qt Creator source code release and build it from scratch.

为了构建该 Qt Creator 插件,你将需要下载 Qt Creator 源代码并且从零开始构建它。

Then, go into the qtcreator directory in the Ekam repo and do the following:

然后,进入 Ekam 版本库的 qtcreator 目录并执行下列语句:

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.

这将构建该插件并将其直接安装到你构建的 Qt Creator 中。

Now you can start Qt Creator and choose the "Ekam Actions" view in the navigation frame.

现在你可以启动 Qt Creator 并在导航框中选择“Ekam Actions”视图。

The plugin connects to a local Ekam instance running on port 41315; so, start ekam like this:

该插件连接到一个在端口41315运行的本地 Ekam 实例;所以,像这样启动 ekam :

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

在你自己的代码中使用 Ekam

### Preferred project layout

推荐项目布局

An Ekam project directory must contain a directory called src which contains all source code.

一个 Ekam 项目目录必须包含一个名为 src 的目录,并在其中包含全部源代码。

Ekam will ignore everything other than this src directory.

Ekam 会忽略所有东西,除了 src 目录。

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.

Ekam 将创建同级的名为 tmpbinlib 的目录。在开始时你或许不应该在这些目录中存在任何目录,因为这便于对其使用 rm -rf 进行清理。

### 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.

要使 Ekam 有能力构建一个项目,它需要获取存放在 Ekam 本身源代码的 src/ekam/rules 目录下的规则文件。

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.

这并不需要特别的位置,不过 src/ekam-rules 其实就不错。

### Compiler flags

编译器标志

You can set the following environment variables to control how Ekam compiles your code:

你可以通过设置以下环境变量来控制 Ekam 编译代码的方式:

* CXX:

  • CXX:

Sets the C++ compiler, e.g. CXX=clang++.

设置 C++ 编译器,比如 CXX=clang++

* CXXFLAGS:

  • CXXFLAGS:

Sets C++ compilation flags, e.g. CXXFLAGS=-std=c++11 -O2 -Wall.

设置 C++ 编译器标志,比如 CXXFLAGS=-std=c++11 -O2 -Wall

* LIBS:

  • LIBS:

Sets linker flags, e.g. LIBS=-lsodium -lz

设置链接器标志,比如 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.

当 Ekam 发现或者编译了一个定义了 main 符号的 .o 文件的时候,它会尝试将该文件依次链接到其它定义了所需符号的 .o 文件以产生一个二进制文件。

The binary takes the name of the .o with the extension removed, e.g. foo.o produces a binary foo.

该二进制文件使用去掉扩展名的 .o 文件的名字,例如, foo.o 会生成二进制文件 foo

The binary is initially just dropped into the tmp directory, which mirrors the src directory.

该二进制文件一开始会放在 tmp 目录中,就像在 src 目录中那样。

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.

例如, src/foo/bar.c++ 会被编译为 src/foo/bar.o,如果它定义了 main 符号,会被链接为 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.

如果你想把二进制文件输出到 bin 目录,你需要定义一个以 .ekam-manifest 作为扩展名的 mainfest 文件。

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."

“一旦你构建了 bar (在该目录中),将其拷贝到 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."

“一旦你构建了 bar (在该目录中),将其拷贝到 bin/baz 下。”

### Building libraries

构建库

Currently, Ekam does not support building libraries.

现在,Ekam 尚未支持构建库。

This seems complicated to support since there's no automated way for Ekam to decide what makes a good library.

支持它显得比较复杂,因为 Ekam 还没法自行确定如何构建一个好的库。

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.

现在,Ekam 仅适合那些最终输出是二进制文件的项目。

A "library" in Ekam is just a directory that contains some code.

Ekam 中的"library"仅仅是一个包含了某些代码的目录。

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.

它不会被构建为一个 .a.so 文件,相反,这些目标文件会在需要的时候被直接链接到其它目录中找到的二进制文件。

Handling Magic Singleton Registries

处理魔术 singleton registry

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.

在 C++ 代码中有种常见的模式,一些文件中的符号不被其它文件所引用,相反这个文件在程序启动的时候把自己注册到某些全局注册表中,使它的功能可以被程序的其它部分使用。

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.

默认情况下,这种模式与 Ekam 不兼容,因为这些符号没有被其它 .o 文件引用,Ekam 没有办法判定这些神奇的自注册文件应该被链接到哪些二进制文件。

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.

例如,对于你想注册的所有魔术模块,让你的 main() 显式调用 registerFoo()registerBar(), 等等。最好传递一个 registry 对象给每一个函数,这样你再也不需要 singleton registry了。

See Singletons Considered Harmful for extended discussion.

参见 Singletons Considered Harmful 了解进一步讨论的情况。

(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:

如果你想 gdb 在调试由 Ekam 构建的二进制文件时能够找到你的代码,你应该像下面那样建立一系列的符号链接:

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.

要理解这些符号链接的含义,参见本文后面关于 intercept.so 的说明。

### 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.

如果 Ekam 构建了一个以 -test_test 结尾的二进制文件,它会运行该文件并根据退出状态报告测试通过或者失败。

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:

Ekam 对于两个测试框架有内置的特别支持。

Google Test and KJ tests.

Google Test 和 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.

当 Ekam 编译一个使用了其中一个框架来声明测试用例(比如,使用 Google Test 的 TESTTEST_F 宏,或者 KJ 的 KJ_TEST 宏)但没有 main 函数的源文件时,它会自动将其链接到对应框架的提供了 main 函数的测试运行器以生成一个测试程序。

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).

要正确集成 Google Test 或 KJ test ,对应测试框架的代码必须作为依赖存放于你的源码树中。

Note that tests are run with intercept.so injected, which has implications if your test does any filesystem access.

注意运行测试时 intercept.so 会被注入,如果你的测试会访问文件系统的话,这可能会有影响。

See the explanation of intercept.so later in this document.

参见本文后面关于 intercept.so 的说明。

### 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.

如果你的项目依赖于其它项目,并且你想将这些外部项目编译为你自己项目构建的一部分(而不是要求用户在他们的用户上安装这些库),你应该遵照像 Ekam 这样的模式。

The basic idea is to have a directory called deps into which you download the repositories of your external dependencies.

这个模式的基本理念是设立一个名为 deps 的目录,并且你要将外部依赖的版本库下载到里面。

Then, under src, place symlinks that deep-link into deps and pull out the code you want.

然后,在 src 目录下放置深层链接到 deps 中的符号链接,之后你就可以引用你想要的代码了。

For example, to depend on Cap'n Proto, you would do something like:

例如,要依赖 Cap'n Proto,你应该像这样:

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.

注意 deps 目录不应该包含到你的版本库中。

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.

对于 Ekam 自身的构建,我们在 Makefile 中处理这个问题,所以你可以看下那个作为例子。

We may eventually add functionality to Ekam to handle dependencies so that you no longer need a Makefile for this.

我们最终会在 Ekam 中添加处理依赖的功能,这样你再也不需要为了这个问题而用 Makefile了。

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:

令人喜大普奔的时候到了。 如果你工作在一堆不同的项目上,与其像上面说的那样嵌套依赖项,你不如直接连接 deps..,这样你的git克隆皆兄弟也。

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 sybling to Ekam.

你可以把这个用在 Ekam 上,例如,你恰好克隆了 Cap'n Proto 作为 Ekam 的同级项目。

### Non-Ekam-clean Dependencies

非 Ekam 式依赖项

If your dependency is not organized for Ekam, you might need to link to its top-level directory rather than a source subdirectory.

如果你的依赖项并未按 Ekam 那样组织,你需要链接到其顶级目录而不只是包含含了源代码的子目录。

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.

例如,Google Test 将其源代码分开放置于 srcinclude 目录中,这样如果你只链接了它的 src 目录,你会找不到那些头文件。

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.

当 Ekam 看到一个名为 include 的目录时,对于它编译的其它代码它都会把这个目录加到引用路径中。

Thus the Google Test headers will end up includable by your code.

因此,Google Test 的头文件就能被你的代码正常地引用了。

Note that projects imported this way will usually have a bunch of errors reported since they are not Ekam-clean.

注意通过这种方式导入的项目通常会报告一堆错误,因为它们不是 Ekam 式的。

However, often (such as in the case of Google Test) the important parts will compile well enough to use.

不过,一般来说(就比如 Google Test )重要部分都能够很好地编译,这就够用了。

### 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.

要额外指定某些应用在特定目录(和递归应用到其子目录)的编译选项,你需要创建一个名为 compile.ekam-flags 的文件并且在其中以 Shell 语法对一些变量进行赋值。

For example:

例如:

# Define FOO to 1 when compiling code in this directory.

# 当编译这个目录中的代码时定义 FOO 为 1 
CXXFLAGS=$CXXFLAGS -DFOO=1 

.ekam-flags files are actually executed using /bin/sh just before invoking the compiler.

.ekam-flags 文件会在调用编译器之前使用 /bin/sh 执行。

When multiple flags files are in-scope, the flags file in the outermost directory runs first.

当有多个标志文件存在于作用域中时,最外层目录中的标志文件首先运行。

## 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.

你可以通过创建一个规则文件来教 Ekam 如何处理一种新的文件类型——或引入一个一次性的不被任何文件触发的规则。

A rule file is any executable with the extension .ekam-rule.

规则文件是以 .ekam-rule 作为扩展名的任意可执行文件。

Often, they are shell scripts, but this is not a requirement.

通常情况下它们是 Shell 脚本,但这不是必须的。

Rule files can themselves be the output of other rules, so you could compile a C++ program that acts as a rule.

规则文件自身可以是其它规则的输出,所以你可以编译一个 C++ 程序用作一条规则。

Ekam executes custom rules and then communicates with them on stdin/stdout using a simple line-based text protocol described below.

Ekam 会运行自定义规则文件,然后用下面描述的一个简单的基于文本行的协议,通过标准输入输出与它们进行通信。

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.

当 Ekam 遇到一个以 .ekam-rule 为扩展名的可执行文件时,它会首先以无参数的方式运行它,从而“学习”它。

At that time, the program may tell ekam what kinds of files it should trigger on using the trigger command (below).

这时,程序会使用 trigger 命令(下面会提到)告诉 ekam 它的触发条件是哪种文件。

When Ekam encounters a trigger file, it will execute the rule again with the trigger file's canonical name as the argument.

当 Ekam 遇到一个符合触发条件的文件时,它会以该文件的规范名称作为参数再次运行该规则。

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.

一个文件的“规范”名称是其去掉了 src/tmp/ 前缀的名字。

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.

不存在拥有相同规范名称的两个文件——不管是构建动作输出了一个已经存在于 src 目录中的文件,还是两个动作在 tmp 目录下生成了相同的文件,这些都是错的。

You can ask Ekam to map a canonical name to a physical disk name using the findInput command.

你可以使用 findInput 命令要求 Ekam 将一个规范名称映射到一个物理磁盘上的名称。

### Tags

标签

Ekam rules are triggered by tags.

Ekam 规则可以由标签来触发。

A tag is a simple string, conventionally of the format type:value.

标签是一个简单的字符串,通常格式是 type:value

Tags are applied to files.

标签会被应用到文件上。

For example, a .o file declaring the symbol main might be given the tag c++symbol:main.

例如,一个定义了 main 符号的 .o 文件会被赋予 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:

Ekam 使用一些默认的规则来赋予可被广泛使用的标签:

* canonical:<filename>:

  • canonical:<filename>:

Each file receives this tag, where <filename> is its canonical name.

每个文件都会收到这种标签,其中 <filename> 指它的规范名称。

* filetype:<extension>:

  • filetype:<extension>

Each regular file that has a file type extension receives this tag, e.g. filetype:.c++.

每个拥有文件类型扩展名的普通文件都会收到这种标签,例如 filetype:.c++

* directory:*:

  • directory:*

Each directory receives this tag.

每个目录都会收到这种标签。

### Commands

命令

A rule may perform the following commands by writing them to standard output.

规则可以通过将其写到标准输出的方式来执行下列命令。

* trigger <tag>:

  • trigger <tag>

Used during the learning phase to tell Ekam that the rule should be executed on any file tagged with <tag>.

用在学习阶段中以通知 Ekam 该规则应针对标有 <tag> 标签的文件执行。

* verb <text>:

  • 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.

用在学习阶段中以告知 Ekam 该规则的 “动词”,之后运行这个规则时,它会被显示给用户。

This should be a simple, descriptive word.

这应当是一个简洁的描述性词语。

For instance, for a C++ compile action, the verb is compile.

例如,对于一个 C++ 编译动作,这个动词应该是 compile

* silent:

  • 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>:

  • 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 会将包含了该文件的完整磁盘路径(比如,包含了 src/tmp/ 的)的一行写到该规则的标准输入中作为回复。

Ekam will remember that the build action depended on this file, so if the file changes, the action will be re-run.

Ekam 会记住这一构建动作依赖于该文件,所以文件改变时,该动作会被再次运行。

If no match was found, Ekam will return a blank line.

如果找不到任何匹配,Ekam 会返回一个空行。

* findProvider <tag>:

  • findProvider <tag>

Find a file tagged with <tag>.

搜索一个拥有 <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.

如果存在多个匹配,Ekam 会启发式地选择最合适的一个,通常是在目录树中离触发规则的文件最近的那个。

The path is returned as with findInput.

路径会像 findInput 那样返回。

Also as with findInput, the file is considered a dependency of the action.

这个文件也会像 findInput 那样被视为该动作的一个依赖项。

Ekam will re-run this action if the file changes or if the file Ekam chose to match <tag> changes.

如果被 Ekam 视为匹配 <tag> 的文件改变了,Ekam 会再次运行该动作。

* findModifiers <name>:

  • findModifiers <name>

Search for the file <name> in the trigger file's directory and every parent up to the source root.

在触发规则的文件所在目录及其逐层父目录中搜索文件 <name> ,一直到源代码的根目录。

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.

例如,compile.ekam-flags 就是用这种方式实现的。

* noteInput <external-file>:

  • noteInput <external-file>:

Tells Ekam that the action depends on <external-file>, which is a path outside of the project's source tree.

通知 Ekam 该动作依赖于外部文件 <external-file>,这个参数是该项目源码树以外的一个路径。

For instance, /usr/include/stdlib.h.

例如, /usr/include/stdlib.h

Currently Ekam ignores this, but in theory it could watch these files and re-run the action if they change.

目前 Ekam 忽略此项,但理论上它应该监视这些文件并且在它们变化时重新运行该动作。

* newOutput <canonical-name>:

  • 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.

Ekam 将该文件实际创建的磁盘路径作为回复写入该规则的标准输入流。

* provide <filename> <tag>:

  • provide <filename> <tag>

Tag <filename> (a canonical name) with <tag>.

<filename> (规范名称)赋予标签 <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.

这个文件必须是已知的输入或者该规则的输出,也就是说,它必须来自之前对于 findInputfindProvider 或者 newOutput 的调用。

* install <filename> <location>:

  • install <filename> <location>

Take the canonical filename <filename> and copy it to <location>, where <location> should start with bin/, lib/, etc.

接收一个文件的规范名称 <filename> 并且将其复制到 <location>,其中 <location> 应该以 bin/lib/ 等开头。

* passed:

  • passed

Indicate that this action ran a test, and the test passed.

表明该动作进行一项测试,并且测试通过。

### intercept.so

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 #includeed by the source file.

例如,运行一个 C++ 编译器需要输入源文件 #include 的所有头文件。

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.

为了解决这个问题,Ekam 实现了可以通过 LD_PRELOAD 注入到构建工具中的库,从而拦截文件系统调用,自动触发合适的 Ekam 命令以注册输入文件和输出文件,然后将调用映射到实际磁盘路径。

The interceptor library is implemented in src/ekam/rules/intercept.c and built by src/ekam/rules/intercept.ekam-rule.

这个拦截库实现在 src/ekam/rules/intercept.c 并且根据 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.

你可以通过获取标签 special:ekam-interceptor 以获得映射到编译好的 intercept.so 库,然后你可以使用 LD_PRELOAD 将其注入到任意的命令中。

See src/ekam/rules/compile.ekam-rule to see how this is done with the C++ compiler.

参见 src/ekam/rules/compile.ekam-rule ,看看它是如何与 C++ 编译器一起工作的。

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.

比如说,在处理 src/foo/bar/baz 的时候,如果构建工具尝试打开 qux/corge,它会被映射到 src/foo/bar/qux/corgesrc/qux/corgesrc/grault/qux/corge,但不会是 src/grault/corgesrc/qux/corge/graultsrc/corge

If you want to open a file by tag, the special virtual path /ekam-provider/<tag-type>/<tag-value> can be used.

如果你想要通过标签打开一个文件,会使用特定的虚拟路径 /ekam-provider/<tag-type>/<tag-value>

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.

比如说,由于 include.ekam-rule 规则会给所有 C++ 头文件加上标签 c++header:<include-path>,当我们加上拦截器来启动编译器时,我们会传递一个标志 -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>.

如果你想单纯根据其完整规范路径来打开一个文件(而不是启发式地搜索邻近的文件),你可以通过打开 /ekam-provider/canonical/<canonical-name> 做到,这是因为每个文件都会有一个 canonical:<canonical-name> 的标签,就像之前说的那样。

## Get Involved

参与进来

Have a question about Ekam, or want to contribute?

对 Ekam 有疑问,或者想做点贡献?

Talk to us on the Ekam discussion group.

Ekam discussion group 上与我们讨论。

Ekam is currently developed as part of the Sandstorm.io project and is primarily used by Sandstorm.

现在 Ekam是作为 Sandstorm.io 项目的一部分进行开发并且也主要用于 Sandstorm.。

If you like Ekam, consider getting involved with Sandstorm.

如果你喜欢 Ekam,不妨考虑 参与 Sandstorm.

@halfcoder
Copy link
Author

原文在此:ekam/README.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment