This document describes how to add a new compiler type (e.g. rustc, swift) to Goma.
You have to define 4 components to add a new type of compiler,
CompilerFlags
CompilerInfo
IncludeProcessor
ExecReqNormalizer
and 1 class to collect compiler type specific code into one place.
CompilerTypeSpecific
As an example, we have defined Fake
type. It pretends to be a compiler, so we can use it as a test.
Note: since Goma has evolved with C/C++, we still have C/C++ specific code here and there, even though we have refactored our code a lot. However, code for new compiler types should be separated from the Goma core code.
Understand the rough procedure of Goma, and what kind of components are necessary to implement a new compiler type.
Here, we consider the following command line as an example.
$ gomacc clang++ -Ibar -Ibaz -std=c++11 -c foo.cc -o foo.o
This command line is passed to compiler_proxy
(via ExecReq
proto) from gomacc
. The first argument gomacc
is dropped when compiler_proxy
receives the command line.
In compiler_proxy
, the following processed will be performed.
CompilerFlags
, which is a parsed result of command line, is made from the command line.CompilerFlags
.-Ibar
, -Ibaz
, -std=c++11
, -c
, and -o foo.o
. Input file is foo.cc
, and output file is foo.o
. Output directory is not specified in this example.CompilerInfo
.CompilerInfoBuilder
collects SHA256 digest of compiler, compiler version (e.g. clang 7.0
), compile target (e.g. x86_64-unknown-linux
), etc. For C/C++, predefined macros are also collected.compiler_info_flags
, cwd
, and local_compiler_path
. compiler_info_flags
is the flags (used in the command line) which affects CompilerInfo. The detail is described in CompilerFlags section.IncludeProcessor
, which lists input files that will be used for each compile request.ExecReq
.compiler_proxy
sends a compile request to Goma server (via ExecReq
proto), and receives the compile result (via ExecResp
proto). If LocalOutputCache
is enabled, compiler_proxy
caches ExecResp
result in local storage. The key of LocalOutputCache
is ExecReq
normalized with ExecReqNormalizer
.Actually there are a few more steps (e.g. to modify command line, to modify upload files, and to modify compile results) to absorb client/server environment difference. If you have to modify them, see client/compile_task.cc.
Note that we use CompilerTypeSpecific
class to call several compiler type specific procedures.
CompilerFlags
is the parsed result of a command line. It receives a compiler command (e.g. clang++ -Ibar -Ibaz -std=c++11 -c foo.cc -o foo.o
), and parses it.
CompilerFlags
collects input/output files (or sometimes output directories), compiler flags, and compiler_info_flags
from the command line.
compiler_info_flags
is a set of compiler_flags that can affect CompilerInfo
. All compiler flags that can affect CompilerInfo must be stored in compiler_info_flags
. In C/C++, CompilerInfo
contains predefined macros e.g. __cplusplus
. Since -std=c++11
affects the value of the predefined macros, -std=c++11
must be listed as compiler_info_flags
. See CompilerInfo section to understand what are defined in CompilerInfo.
CompilerFlagType
(lib/compiler_flag_type.h).<Language>Flags
which is derived from CompilerFlags
in lib/<language>_flags.h
lang_
. C/C++ compiler often provides a language switch (e.g. -x
). To make the rest of procedures easier, we have lang_
. If the compiler accepts only one language, just set it. For example, lang_
would be c
, c++
, or java
.compiler_info_flags
.true
to is_successful_
.IsServerImportantEnv()
if a compiler requires to use envvars. Only envvars where IsServerImportantEnv
returns true are passed to Goma server.Is<Language>Command
, and GetCompilerName
(see FakeFlags
for example).example
CompilerInfo
contains the information how the compiler is configured. The builder class CompilerInfoBuilder
is separated from CompilerInfo
,
CompilerInfo
has the following information. This is not the complete list. See client/compiler_info.h for more information.
subprograms
. They are programs that the compiler might use during a compile. For example, objcopy
. It's used to identify which subprogram is used.additional_flags
. Command line flags automatically added for remote compile. They are used to absorb client/server environment difference.resource
: Files, which are not defined in user's command line and not listed with include processor, but necessary for remote compile. Some compiler implicitly uses a resource file (e.g. if -fsanitize=address
is passed to clang
, asan_blacklist.txt
is implicitly used.)For C/C++, CompilerInfo
also has system include directories (e.g. -isystem
), and compiler predefined macros (e.g. __linux__
). See client/cxx/cxx_compiler_info.h for more information.
CompilerInfoType
(client/compiler_info.h).CompilerInfoType
. Even if no extension data is required, we have to define an empty type here.<Language>CompilerInfo
in client/<language>/<language>_compiler_info.h
CompilerInfoType
, so this is easy.<Language>CompilerInfoBuilder
in client/<language>/<language>_compiler_info_buidler.h
SetTypeSpecificCompilerInfo()
. In this method,CompilerInfoData
with language extension typeCompilerInfoBuilder
).example
IncludeProcessor
lists input files used in a compile. IncludeProcessor
receives CompilerInfo
, CompilerFlags
, (CommandSpec
,) and returns the file list which will be used during the compile.
For C/C++, we have our own C preprocessor to list all header files used for a compile. For java, it checks .jar
files to list dependent .jar files.
<Language>IncludeProcessor
in client/<language>/<languagle>_include_processor.h
<Language>CompilerTypeSpecific
class (which is described later), implement RunIncludeProcessor
.example
ExecReqNormalizer
normalizes ExecReq
for LocalOutputCache
. LocalOutputCache
can store the compile result in local storage (like ccache). The key of the LocalOutputCache
is the SHA256 digest of the normalized ExecReq
.
If it is sure some fields of ExecReq
does not affect compile result, they can be normalized with ExecReqNormalizer
.
For example, when the compiler is clang
and the command line contains -g0
(= omit debug information), then cwd
can be removed from the normalized ExecReq
.
If you not sure, just keep ExecReq
as is.
<Language>ExecReqNormalizer
in lib/<language>_execreq_normalizer.h
ConfigurableExecReqNormalizer
. If not sure, just return Config::AsIs()
from Configure()
method. This doesn't normalize anything, however, this should be ok for the first step.example
CompilerTypeSpecific
is a collection of methods to call compiler type specific processes in compiler_proxy
.
<Language>CompilerTypeSpecific
in client/<language>/<language>_compiler_type_specific.h
<Language>CompilerTypeSpecific
should be derived from CompilerTypeSpecific
.CompilerTypeSpecificCollection
in client/compiler_type_specific_collection.h
example
You must want to check these components are working correctly. The one way to check is to run the new type of compiler with Goma.
compiler_proxy
(goma_ctl.py ensure_start
)GOMA_USE_LOCAL=false
GOMA_USE_LOCAL=false gomacc <compiler_path> …
IncludeProcessor
runs).Then, open compiler_proxy
dashboard http://localhost:8088/. A new task must be shown in the dashboard. Probably, your task is shown as GOMA ERROR
(red color) if your compiler is not registered in Goma server. GOMA ERROR
means the compile failed in the remote, but succeeded locally. If it‘s shown as FAILURE
(pink color), your compile failed locally, too. This indicates something is wrong, but we have to see compiler_proxy
log http://localhost:8088/logz?INFO to investigate what’s really wrong.
http://localhost:8088/compilerinfoz shows the list of CompilerInfo
. Find [compiler info]
section, and find your compiler from the list.
Open your compile task in the dashbaord http://localhost:8088/. inputs
shows the list of files which is sent to Goma server. If your IncludeProcessor
didn't work correctly, some files might be missing.
For CompilerFlag
, currently we don't have a good interface to see the details in compiler_proxy
dashboard. We recommend you write a unittest.