[インデックス 12699] ファイルの概要
このコミットは、Go言語のランタイムから goc2c.c
というファイルを削除するものです。このファイルの機能は cmd/dist
に移行されたため、不要になったと説明されています。
コミット
commit 4b1933dfb2a332a6a9744c7d5b05ea08f9909658
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Mar 21 00:49:45 2012 +0800
runtime: remove unused goc2c.c
Its functionality has been moved into cmd/dist.
R=golang-dev, r, iant
CC=golang-dev
https://golang.org/cl/5843062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4b1933dfb2a332a6a9744c7d5b05ea08f9909658
元コミット内容
runtime: remove unused goc2c.c
Its functionality has been moved into cmd/dist.
変更の背景
このコミットの背景には、Go言語のビルドシステムとランタイムの進化があります。goc2c.c
は、GoのコードとCのコードを組み合わせた .goc
ファイルをCファイルに変換するためのツールでした。これは、Goの初期段階において、GoとCの相互運用性、特にGoランタイムの一部をCで記述し、それをGoから呼び出すためのメカニズムとして使用されていました。
しかし、Goのビルドシステムは時間の経過とともに成熟し、より統合されたツールチェーンへと移行していきました。特に、cmd/dist
はGoのソースコードからGoのツールチェーン全体をビルドするための主要なコマンドであり、Goのビルドプロセスにおける様々なステップ(コンパイル、リンク、アセンブルなど)を管理します。
goc2c.c
の機能が cmd/dist
に移行されたということは、.goc
ファイルの処理が、Goの公式ビルドツールチェーンの一部として、より効率的かつ統合された方法で扱われるようになったことを意味します。これにより、独立した変換ツールとしての goc2c.c
は不要となり、コードベースの整理と簡素化が図られました。
前提知識の解説
Go言語のランタイム (runtime)
Go言語のランタイムは、Goプログラムの実行を管理する非常に重要な部分です。これには、ガベージコレクション、スケジューラ(ゴルーチンの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goのランタイムは、Goプログラムが効率的かつ並行に動作するための基盤を提供します。ランタイムの一部はGoで書かれていますが、パフォーマンスや低レベルのシステム操作のためにCやアセンブリ言語で書かれている部分もあります。
goc2c.c
の役割 (削除前)
goc2c.c
は、Goの初期のビルドプロセスにおいて、GoとCのハイブリッドコードを扱うためのツールでした。具体的には、Goの関数シグネチャを持つCコードを記述できる .goc
ファイルを、通常のCコンパイラでコンパイル可能な .c
ファイルに変換する役割を担っていました。これにより、Goのランタイムや標準ライブラリの一部で、C言語の既存のコードや低レベルの操作をGoから直接呼び出すことが可能になっていました。
.goc
ファイルの構造は、Goのパッケージ宣言、Cのプリプロセッサディレクティブ、そしてGoの関数シグネチャに続くCコードブロックで構成されていました。goc2c.c
はこれらのファイルを解析し、Goの呼び出し規約に合わせたC関数を生成することで、GoとCの間のブリッジを提供していました。
cmd/dist
cmd/dist
は、Go言語のソースコードからGoのツールチェーン全体(コンパイラ、リンカ、アセンブラ、その他のユーティリティなど)をビルドするためのコマンドです。これはGoのビルドシステムの中核をなし、Goのバージョン管理、クロスコンパイル、および様々なプラットフォームへのGoのデプロイを容易にします。
cmd/dist
は、Goのソースツリー内の様々なコンポーネントをビルドするためのルールとロジックを含んでいます。goc2c.c
の機能が cmd/dist
に移行されたということは、.goc
ファイルの変換プロセスが、Goの公式ビルドプロセスの一部として、cmd/dist
によって直接管理されるようになったことを意味します。これにより、ビルドの自動化、依存関係の管理、およびクロスプラットフォームビルドのサポートが向上します。
技術的詳細
このコミットは、Goのビルドシステムにおける重要な変更を反映しています。goc2c.c
は、Goの初期のビルドプロセスにおいて、GoとCの相互運用性を実現するためのカスタムツールでした。その主な機能は、Goの関数シグネチャを持つCコードを記述できる .goc
ファイルを、標準的なCコンパイラで処理できる .c
ファイルに変換することでした。
goc2c.c
の内部では、Goの関数定義を解析し、Goの呼び出し規約(引数の渡し方、戻り値の扱いなど)に準拠したCのラッパー関数を生成していました。これにより、GoのコードからCの関数を透過的に呼び出すことが可能になっていました。また、コメントのスキップ、トークンの読み取り、型変換(Goの型からCの型へ)などの処理も行っていました。
このコミットで goc2c.c
が削除されたのは、その機能が cmd/dist
に統合されたためです。これは、Goのビルドプロセスがより洗練され、Goのツールチェーン自体がGoとCの相互運用性をよりネイティブにサポートするようになったことを示唆しています。具体的には、cmd/dist
が .goc
ファイルの変換ロジックを内部的に処理するようになったか、あるいは .goc
ファイルの概念自体が、より現代的なGoの外部関数インターフェース(FFI)やCgoのメカニズムに置き換えられた可能性があります。
この変更は、Goのビルドプロセスの簡素化と効率化に貢献します。独立した変換ツールが不要になることで、ビルドスクリプトの複雑さが軽減され、Goのツールチェーン全体の一貫性が向上します。また、Goのランタイムコードベースから不要なCコードが削除されることで、メンテナンス性が向上し、Goのコア部分の理解が容易になります。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、単一のファイルの削除です。
src/pkg/runtime/goc2c.c
が削除されました。
変更の差分は以下の通りです。
--- a/src/pkg/runtime/goc2c.c
+++ /dev/null
@@ -1,750 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-/*
- * Translate a .goc file into a .c file. A .goc file is a combination
- * of a limited form of Go with C.
- */
-
-/*
- package PACKAGENAME
- {# line}
- func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{
- C code with proper brace nesting
- \}
-*/
-
-/*
- * We generate C code which implements the function such that it can
- * be called from Go and executes the C code.
- */
-
-#include <u.h>
-#include <stdio.h>
-#include <libc.h>
-
-/* Whether we're emitting for gcc */
-static int gcc;
-
-/* File and line number */
-static const char *file;
-static unsigned int lineno = 1;
-
-/* List of names and types. */
-struct params {
- struct params *next;
- char *name;
- char *type;
-};
-
-/* index into type_table */
-enum {
- Bool,
- Float,
- Int,
- Uint,
- Uintptr,
- String,
- Slice,
- Eface,
-};
-
-static struct {
- char *name;
- int size;
-} type_table[] = {
- /* variable sized first, for easy replacement */
- /* order matches enum above */
- /* default is 32-bit architecture sizes */
- "bool", 1,
- "float", 4,
- "int", 4,
- "uint", 4,
- "uintptr", 4,
- "String", 8,
- "Slice", 12,
- "Eface", 8,
-
- /* fixed size */
- "float32", 4,
- "float64", 8,
- "byte", 1,
- "int8", 1,
- "uint8", 1,
- "int16", 2,
- "uint16", 2,
- "int32", 4,
- "uint32", 4,
- "int64", 8,
- "uint64", 8,
-
- NULL,
-};
-
-/* Fixed structure alignment (non-gcc only) */
-int structround = 4;
-
-/* Unexpected EOF. */
-static void
-bad_eof(void)
-{
- sysfatal("%s:%ud: unexpected EOF\n", file, lineno);
-}
-
-/* Out of memory. */
-static void
-bad_mem(void)
-{
- sysfatal("%s:%ud: out of memory\n", file, lineno);
-}
-
-/* Allocate memory without fail. */
-static void *
-xmalloc(unsigned int size)
-{
- void *ret = malloc(size);
- if (ret == NULL)
- bad_mem();
- return ret;
-}
-
-/* Reallocate memory without fail. */
-static void*
-xrealloc(void *buf, unsigned int size)
-{
- void *ret = realloc(buf, size);
- if (ret == NULL)
- bad_mem();
- return ret;
-}
-
-/* Free a list of parameters. */
-static void
-free_params(struct params *p)
-{
- while (p != NULL) {
- struct params *next;
-
- next = p->next;
- free(p->name);
- free(p->type);
- free(p);
- p = next;
- }
-}
-
-/* Read a character, tracking lineno. */
-static int
-getchar_update_lineno(void)
-{
- int c;
-
- c = getchar();
- if (c == '\n')
- ++lineno;
- return c;
-}
-
-/* Read a character, giving an error on EOF, tracking lineno. */
-static int
-getchar_no_eof(void)
-{
- int c;
-
- c = getchar_update_lineno();
- if (c == EOF)
- bad_eof();
- return c;
-}
-
-/* Read a character, skipping comments. */
-static int
-getchar_skipping_comments(void)
-{
- int c;
-
- while (1) {
- c = getchar_update_lineno();
- if (c != '/')
- return c;
-
- c = getchar();
- if (c == '/') {
- do {
- c = getchar_update_lineno();
- } while (c != EOF && c != '\n');
- return c;
- } else if (c == '*') {
- while (1) {
- c = getchar_update_lineno();
- if (c == EOF)
- return EOF;
- if (c == '*') {
- do {
- c = getchar_update_lineno();
- } while (c == '*');
- if (c == '/')
- break;
- }
- }
- } else {
- ungetc(c, stdin);
- return '/';
- }
- }
-}
-
-/*
- * Read and return a token. Tokens are string or character literals
- * or else delimited by whitespace or by [(),{}].
- * The latter are all returned as single characters.
- */
-static char *
-read_token(void)
-{
- int c, q;
- char *buf;
- unsigned int alc, off;
- const char* delims = "(),{}";
-
- while (1) {
- c = getchar_skipping_comments();
- if (c == EOF)
- return NULL;
- if (!isspace(c))
- break;
- }
- alc = 16;
- buf = xmalloc(alc + 1);
- off = 0;
- if(c == '"' || c == '\'') {
- q = c;
- buf[off] = c;
- ++off;
- while (1) {
- if (off+2 >= alc) { // room for c and maybe next char
- alc *= 2;
- buf = xrealloc(buf, alc + 1);
- }
- c = getchar_no_eof();
- buf[off] = c;
- ++off;
- if(c == q)
- break;
- if(c == '\\') {
- buf[off] = getchar_no_eof();
- ++off;
- }
- }
- } else if (strchr(delims, c) != NULL) {
- buf[off] = c;
- ++off;
- } else {
- while (1) {
- if (off >= alc) {
- alc *= 2;
- buf = xrealloc(buf, alc + 1);
- }
- buf[off] = c;
- ++off;
- c = getchar_skipping_comments();
- if (c == EOF)
- break;
- if (isspace(c) || strchr(delims, c) != NULL) {
- if (c == '\n')
- lineno--;
- ungetc(c, stdin);
- break;
- }
- }
- }
- buf[off] = '\0';
- return buf;
-}
-
-/* Read a token, giving an error on EOF. */
-static char *
-read_token_no_eof(void)
-{
- char *token = read_token();
- if (token == NULL)
- bad_eof();
- return token;
-}
-
-/* Read the package clause, and return the package name. */
-static char *
-read_package(void)
-{
- char *token;
-
- token = read_token_no_eof();
- if (token == nil)
- sysfatal("%s:%ud: no token\n", file, lineno);
- if (strcmp(token, "package") != 0) {
- sysfatal("%s:%ud: expected \"package\", got \"%s\"\n",
- file, lineno, token);
- }
- return read_token_no_eof();
-}
-
-/* Read and copy preprocessor lines. */
-static void
-read_preprocessor_lines(void)
-{
- while (1) {
- int c;
-
- do {
- c = getchar_skipping_comments();
- } while (isspace(c));
- if (c != '#') {
- ungetc(c, stdin);
- break;
- }
- putchar(c);
- do {
- c = getchar_update_lineno();
- putchar(c);
- } while (c != '\n');
- }
-}
-
-/*
- * Read a type in Go syntax and return a type in C syntax. We only
- * permit basic types and pointers.
- */
-static char *
-read_type(void)
-{
- char *p, *op, *q;
- int pointer_count;
- unsigned int len;
-
- p = read_token_no_eof();
- if (*p != '*')
- return p;
- op = p;
- pointer_count = 0;
- while (*p == '*') {
- ++pointer_count;
- ++p;
- }
- len = strlen(p);
- q = xmalloc(len + pointer_count + 1);
- memcpy(q, p, len);
- while (pointer_count > 0) {
- q[len] = '*';
- ++len;
- --pointer_count;
- }
- q[len] = '\0';
- free(op);
- return q;
-}
-
-/* Return the size of the given type. */
-static int
-type_size(char *p)
-{
- int i;
-
- if(p[strlen(p)-1] == '*')
- return type_table[Uintptr].size;
-
- for(i=0; type_table[i].name; i++)
- if(strcmp(type_table[i].name, p) == 0)
- return type_table[i].size;
- sysfatal("%s:%ud: unknown type %s\n", file, lineno, p);
- return 0;
-}
-
-/*
- * Read a list of parameters. Each parameter is a name and a type.
- * The list ends with a ')'. We have already read the '('.
- */
-static struct params *
-read_params(int *poffset)
-{
- char *token;
- struct params *ret, **pp, *p;
- int offset, size, rnd;
-
- ret = NULL;
- pp = &ret;
- token = read_token_no_eof();
- offset = 0;
- if (strcmp(token, ")") != 0) {
- while (1) {
- p = xmalloc(sizeof(struct params));
- p->name = token;
- p->type = read_type();
- p->next = NULL;
- *pp = p;
- pp = &p->next;
-
- size = type_size(p->type);
- rnd = size;
- if(rnd > structround)
- rnd = structround;
- if(offset%rnd)
- offset += rnd - offset%rnd;
- offset += size;
-
- token = read_token_no_eof();
- if (strcmp(token, ",") != 0)
- break;
- token = read_token_no_eof();
- }
- }
- if (strcmp(token, ")") != 0) {
- sysfatal("%s:%ud: expected '('\n",
- file, lineno);
- }
- if (poffset != NULL)
- *poffset = offset;
- return ret;
-}
-
-/*
- * Read a function header. This reads up to and including the initial
- * '{' character. Returns 1 if it read a header, 0 at EOF.
- */
-static int
-read_func_header(char **name, struct params **params, int *paramwid, struct params **rets)
-{
- int lastline;
- char *token;
-
- lastline = -1;
- while (1) {
- token = read_token();
- if (token == NULL)
- return 0;
- if (strcmp(token, "func") == 0) {
- if(lastline != -1)
- printf("\n");
- break;
- }
- if (lastline != lineno) {
- if (lastline == lineno-1)
- printf("\n");
- else
- printf("\n#line %d \"%s\"\n", lineno, file);
- lastline = lineno;
- }
- printf("%s ", token);
- }
-
- *name = read_token_no_eof();
-
- token = read_token();
- if (token == NULL || strcmp(token, "(") != 0) {
- sysfatal("%s:%ud: expected \"(\"\n",
- file, lineno);
- }
- *params = read_params(paramwid);
-
- token = read_token();
- if (token == NULL || strcmp(token, "(") != 0)
- *rets = NULL;
- else {
- *rets = read_params(NULL);
- token = read_token();
- }
- if (token == NULL || strcmp(token, "{") != 0) {
- sysfatal("%s:%ud: expected \"{\"\n",
- file, lineno);
- }
- return 1;
-}
-
-/* Write out parameters. */
-static void
-write_params(struct params *params, int *first)
-{
- struct params *p;
-
- for (p = params; p != NULL; p = p->next) {
- if (*first)
- *first = 0;
- else
- printf(", ");
- printf("%s %s", p->type, p->name);
- }
-}
-
-/* Write a 6g function header. */
-static void
-write_6g_func_header(char *package, char *name, struct params *params,
- int paramwid, struct params *rets)
-{
- int first, n;
-
- printf("void\n%s·%s(", package, name);
- first = 1;
- write_params(params, &first);
-
- /* insert padding to align output struct */
- if(rets != NULL && paramwid%structround != 0) {
- n = structround - paramwid%structround;
- if(n & 1)
- printf(", uint8");
- if(n & 2)
- printf(", uint16");
- if(n & 4)
- printf(", uint32");
- }
-
- write_params(rets, &first);
- printf(")\n{\n");
-}
-
-/* Write a 6g function trailer. */
-static void
-write_6g_func_trailer(struct params *rets)
-{
- struct params *p;
-
- for (p = rets; p != NULL; p = p->next)
- printf("\tFLUSH(&%s);\n", p->name);
- printf("}\n");
-}
-
-/* Define the gcc function return type if necessary. */
-static void
-define_gcc_return_type(char *package, char *name, struct params *rets)
-{
- struct params *p;
-
- if (rets == NULL || rets->next == NULL)
- return;
- printf("struct %s_%s_ret {\n", package, name);
- for (p = rets; p != NULL; p = p->next)
- printf(" %s %s;\n", p->type, p->name);
- printf("};\n");
-}
-
-/* Write out the gcc function return type. */
-static void
-write_gcc_return_type(char *package, char *name, struct params *rets)
-{
- if (rets == NULL)
- printf("void");
- else if (rets->next == NULL)
- printf("%s", rets->type);
- else
- printf("struct %s_%s_ret", package, name);
-}
-
-/* Write out a gcc function header. */
-static void
-write_gcc_func_header(char *package, char *name, struct params *params,
- struct params *rets)
-{
- int first;
- struct params *p;
-
- define_gcc_return_type(package, name, rets);
- write_gcc_return_type(package, name, rets);
- printf(" %s_%s(", package, name);
- first = 1;
- write_params(params, &first);
- printf(") asm (\"%s.%s\");\n", package, name);
- write_gcc_return_type(package, name, rets);
- printf(" %s_%s(", package, name);
- first = 1;
- write_params(params, &first);
- printf(")\n{\n");
- for (p = rets; p != NULL; p = p->next)
- printf(" %s %s;\n", p->type, p->name);
-}
-
-/* Write out a gcc function trailer. */
-static void
-write_gcc_func_trailer(char *package, char *name, struct params *rets)
-{
- if (rets == NULL)
- ;
- else if (rets->next == NULL)
- printf("return %s;\n", rets->name);
- else {
- struct params *p;
-
- printf(" {\n struct %s_%s_ret __ret;\n", package, name);
- for (p = rets; p != NULL; p = p->next)
- printf(" __ret.%s = %s;\n", p->name, p->name);
- printf(" return __ret;\n }\n");
- }
- printf("}\n");
-}
-
-/* Write out a function header. */
-static void
-write_func_header(char *package, char *name,
- struct params *params, int paramwid,
- struct params *rets)
-{
- if (gcc)
- write_gcc_func_header(package, name, params, rets);
- else
- write_6g_func_header(package, name, params, paramwid, rets);
- printf("#line %d \"%s\"\n", lineno, file);
-}
-
-/* Write out a function trailer. */
-static void
-write_func_trailer(char *package, char *name,
- struct params *rets)
-{
- if (gcc)
- write_gcc_func_trailer(package, name, rets);
- else
- write_6g_func_trailer(rets);
-}
-
-/*
- * Read and write the body of the function, ending in an unnested }
- * (which is read but not written).
- */
-static void
-copy_body(void)
-{
- int nesting = 0;
- while (1) {
- int c;
-
- c = getchar_no_eof();
- if (c == '}' && nesting == 0)
- return;
- putchar(c);
- switch (c) {
- default:
- break;
- case '{':
- ++nesting;
- break;
- case '}':
- --nesting;
- break;
- case '/':
- c = getchar_update_lineno();
- putchar(c);
- if (c == '/') {
- do {
- c = getchar_no_eof();
- putchar(c);
- } while (c != '\n');
- } else if (c == '*') {
- while (1) {
- c = getchar_no_eof();
- putchar(c);
- if (c == '*') {
- do {
- c = getchar_no_eof();
- putchar(c);
- } while (c == '*');
- if (c == '/')
- break;
- }
- }
- }
- break;
- case '"':
- case '\'':
- {
- int delim = c;
- do {
- c = getchar_no_eof();
- putchar(c);
- if (c == '\\') {
- c = getchar_no_eof();
- putchar(c);
- c = '\0';
- }
- } while (c != delim);
- }
- break;
- }
- }
-}
-
-/* Process the entire file. */
-static void
-process_file(void)
-{
- char *package, *name;
- struct params *params, *rets;
- int paramwid;
-
- package = read_package();
- read_preprocessor_lines();
- while (read_func_header(&name, ¶ms, ¶mwid, &rets)) {
- write_func_header(package, name, params, paramwid, rets);
- copy_body();
- write_func_trailer(package, name, rets);
- free(name);
- free_params(params);
- free_params(rets);
- }
- free(package);
-}
-
-static void
-usage(void)
-{
- sysfatal("Usage: goc2c [--6g | --gc] [file]\n");
-}
-
-void
-main(int argc, char **argv)
-{
- char *goarch;
-
- argv0 = argv[0];
- while(argc > 1 && argv[1][0] == '-') {
- if(strcmp(argv[1], "-") == 0)
- break;
- if(strcmp(argv[1], "--6g") == 0)
- gcc = 0;
- else if(strcmp(argv[1], "--gcc") == 0)
- gcc = 1;
- else
- usage();
- argc--;
- argv++;
- }
-
- if(argc <= 1 || strcmp(argv[1], "-") == 0) {
- file = "<stdin>";
- process_file();
- exits(0);
- }
-
- if(argc > 2)
- usage();
-
- file = argv[1];
- if(freopen(file, "r", stdin) == 0) {
- sysfatal("open %s: %r\n", file);
- }
-
- if(!gcc) {
- // 6g etc; update size table
- goarch = getenv("GOARCH");
- if(goarch != NULL && strcmp(goarch, "amd64") == 0) {
- type_table[Uintptr].size = 8;
- type_table[String].size = 16;
- type_table[Slice].size = 8+4+4;
- type_table[Eface].size = 8+8;
- structround = 8;
- }
- }
-
- printf("// AUTO-GENERATED by autogen.sh; DO NOT EDIT\n\n");
- process_file();
- exits(0);
-}
コアとなるコードの解説
このコミットは、src/pkg/runtime/goc2c.c
ファイルを完全に削除しています。このファイルは、Goの初期のビルドプロセスにおいて、GoとCのハイブリッドコードを扱うためのカスタムツールでした。
削除された goc2c.c
の内容は、主に以下の機能を含んでいました。
.goc
ファイルの解析:.goc
ファイルは、Goのfunc
キーワードで始まるCコードブロックを含む特殊なファイル形式でした。goc2c.c
は、このファイルを読み込み、Goの関数シグネチャとCのコードを区別して解析していました。- GoとCの型変換: Goの基本的な型(
int
,string
,slice
など)とCの対応する型の間でサイズやアライメントを考慮した変換ロジックを持っていました。特に、ポインタの扱いや構造体のアライメントに関する処理が含まれていました。 - Cコードの生成: 解析した
.goc
ファイルの内容に基づいて、Goの呼び出し規約に準拠したCのラッパー関数を生成していました。これにより、GoのランタイムがCの関数を呼び出すことが可能になっていました。 - コメントと文字列の処理: Cのコメント(
//
と/* ... */
)や文字列リテラル、文字リテラルを正しくスキップ・処理するロジックが含まれていました。 - コマンドライン引数の処理:
goc2c
ツール自体が、--6g
や--gcc
といったオプションを受け取り、生成するCコードの形式を制御していました。これは、Goの初期のコンパイラ(6g
など)とGCCのような標準Cコンパイラの両方に対応するためでした。
このファイルが削除されたということは、これらの機能がGoのビルドシステム(特に cmd/dist
)のより深い部分に統合されたか、あるいは GoとCの相互運用性のメカニズムが進化し、goc2c
のような中間変換ツールが不要になったことを意味します。例えば、Cgoのようなツールがより成熟し、GoとCの間のインターフェースをより直接的に扱うようになった可能性が考えられます。
この削除は、Goのビルドプロセスがより洗練され、Goのツールチェーンが自己完結的で効率的になったことを示す良い例です。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Goのソースコードリポジトリ: https://github.com/golang/go
- Goのビルドシステムに関する情報 (Goの公式ドキュメントやブログ記事を参照)
参考にした情報源リンク
- コミットのGitHubページ: https://github.com/golang/go/commit/4b1933dfb2a332a6a9744c7d5b05ea08f9909658
- Goのコードレビューシステム (Gerrit) のリンク: https://golang.org/cl/5843062
- Go言語の歴史とビルドプロセスの進化に関する一般的な知識
- Go言語のランタイムとCgoに関する一般的な知識