20201024のGoに関する記事は5件です。

AtCoder Regular Contest 106のメモ

前置き

Atcoderをやってみたので、自分用のメモです。
あとから加筆・修正する予定です。

問題

https://atcoder.jp/contests/arc106

A

Q_A.go
package main

import (
    "fmt"
)

func main() {
    var n int64
    fmt.Scanf("%d", &n)

    var count_a int64 = 0
    var count_b int64 = 0

    var a, b int64 
    flag := true
    for a=3; a<=n; a=a*3{
        count_a += 1
        count_b = 0
        for b=5; b<n-a+1; b=b*5{
            count_b += 1
            if (a+b == n) && flag{
                flag = false
                fmt.Printf("%d %d\n", count_a, count_b)
                break
            }    
        }
    }

    if flag{
        fmt.Printf("%d\n", -1)
    }
}

B

覚えてたら後で書きます。

C

覚えてたら後で書きます。

D

覚えてたら後で書きます。

E

覚えてたら後で書きます。

F

覚えてたら後で書きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Golangのos/execパッケージを利用するときのCD

  • たとえば、2つ上の階層にあるディレクトリにあるMakefileを実行したいとき
os.Chdir("./../../")
cmd := exec.Command("make", "ENV_FILE=.env", "DOMAIN_NAME=vamdemic")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ポインタについてのメモ(Go)

ポインタがどういうものなのか分からなかったので調べた。
今回はGoで書いた。

pointer_practice.go
package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {

    x := 2
    /* 変数xのアドレス(変数xが保存されているメモリの位置)を格納 */
    /* ある変数のアドレスが格納された変数のことをポインタ変数という */
    x_pointer := &x

    fmt.Println(x)
    fmt.Println(x_pointer)
    /* ポインタ変数x_pointerに格納されたアドレスに存在する値を取得 */
    /* メモリをjsのオブジェクト、アドレスをオブジェクトのキーと考えると メモリ[アドレス(x_pointer)] = 2 というイメージ */
    fmt.Println(*x_pointer)

    arg := 0
    arg_pointer := &arg

    test_pointer(arg_pointer)
    /* 同じ結果になる */
    fmt.Println(arg)
    fmt.Println(*arg_pointer)

    /* 値は書き換えられない(関数内では書き換えられるが、引数として設定した変数の値に影響はない) */
    /* returnで関数内での書き換え結果を取得する */
    test_val(arg)
    fmt.Println(arg)

}

/* ポインタ変数を引数として受け取る関数(ポインタ渡し) */
func test_pointer(val_pointer *int) {

    /* 時間を用いてseed(乱数のもと?)を作成 Seedがないと毎回同じ値を取得してしまう */
    rand.Seed(time.Now().UnixNano())
    /* ポインタ変数に格納されたアドレスを取得 -> 値の書き換えを行うメモリの位置をアドレスで指定 -> 指定したメモリの位置に保存されている値を0~999のランダムな数字に書き換える */
    *val_pointer = rand.Intn(1000)

}

/* 値渡しの関数(いつもの) */
func test_val(val int) {

    rand.Seed(time.Now().UnixNano())
    val = rand.Intn(1000)

}

値渡し(変数の値を受け取る)の関数とポインタ渡し(変数のアドレスを受け取る)の関数とでは、引数の型の設定方法が異なる。

/* 値渡しの関数 */
func(引数 引数の型) { /* 関数の処理 */ }
/* ポインタ渡しの関数 型名の前に「*」が付く*/
func(引数 *引数の型) { /* 関数の処理 */ }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GolangでPKI入門 - 4

1. この記事の対象の人

  • Golang で、証明書失効リスト( CRL )を作りたい人

2. 概要

この記事では、
1. Go で秘密鍵と証明書を生成
2. Go で失効させる証明書のリストを生成
3. Go で Issuing Distribution Point のExtensionを作成
4. Go で証明書失効リスト( CRL )を作成
5. OpenSSL で証明書失効リスト( CRL )の中身を確認
します。

3. Golang で自己署名 CA 証明書と秘密鍵を作成

証明書失効リストを発行する自己署名 CA の「証明書」と「秘密鍵」を作成します。
詳細な説明は、GolangでPKI入門 - 2 を参照ください。
証明書失効リスト作成時に引数で必要になるので、秘密鍵はDER形式にしておきます。

    //PrivateKey of Self Sign CA Certificate
    privateCaKey, err := rsa.GenerateKey(rand.Reader, 2048)
    publicCaKey := privateCaKey.Public()

    //[RFC5280]
    subjectCa := pkix.Name{
        CommonName:         "ca01",
        OrganizationalUnit: []string{"Example Org Unit"},
        Organization:       []string{"Example Org"},
        Country:            []string{"JP"},
    }

    caTpl := &x509.Certificate{
        SerialNumber:          big.NewInt(1),
        Subject:               subjectCa,
        NotAfter:              time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
        NotBefore:             time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
        IsCA:                  true,
        KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
        BasicConstraintsValid: true,
    }

    //Self Sign CA Certificate
    caCertificate, err := x509.CreateCertificate(rand.Reader, caTpl, caTpl, publicCaKey, privateCaKey)


    //Convert to ASN.1 DER encoded form
    derCaCert, err = x509.ParseCertificate(caCertificate)
    if err != nil {
        log.Fatalf("ERROR:%v\n", err)
    }

4. Golang で証明書失効リストを作成

失効させる証明書のリストを作成

    var rcs []pkix.RevokedCertificate
    rc := pkix.RevokedCertificate{
        SerialNumber:   big.NewInt(100),
        RevocationTime: time.Now(),
    }

    rcs = append(rcs, rc)

    rc = pkix.RevokedCertificate{
        SerialNumber:   big.NewInt(108),
        RevocationTime: time.Now(),
    }

    rcs = append(rcs, rc)

ここでは、シリアルが100と108の証明書を失効させます。

証明書失効リストの crlExtensions に Issuing Distribution Point を追加

Go で証明書失効リストを作成するときに利用する x509.RevocationList には、直接 Issuing Distribution Point を追加するFieldはありません。
別途 Issuing Distribution Point 用の構造体を作成して、Extensionに追加してやる必要があります。
RFC5280 では、Issuing Distribution Pointは以下のように定義されています。

   id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }

   IssuingDistributionPoint ::= SEQUENCE {
        distributionPoint          [0] DistributionPointName OPTIONAL,
        onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
        onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
        onlySomeReasons            [3] ReasonFlags OPTIONAL,
        indirectCRL                [4] BOOLEAN DEFAULT FALSE,
        onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }

   DistributionPointName ::= CHOICE {
        fullName                [0]     GeneralNames,
        nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }

   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName

   GeneralName ::= CHOICE {
        otherName                       [0]     OtherName,
        rfc822Name                      [1]     IA5String,
        dNSName                         [2]     IA5String,
        x400Address                     [3]     ORAddress,
        directoryName                   [4]     Name,
        ediPartyName                    [5]     EDIPartyName,
        uniformResourceIdentifier       [6]     IA5String,
        iPAddress                       [7]     OCTET STRING,
        registeredID                    [8]     OBJECT IDENTIFIER }

上記に従い、issuingDistributionPoint と distributionPointName を 以下の Go の構造体として定義しました。

// RFC5280, 5.2.5
type issuingDistributionPoint struct {
    DistributionPoint          distributionPointName `asn1:"optional,tag:0"`
    OnlyContainsUserCerts      bool                  `asn1:"optional,tag:1"`
    OnlyContainsCACerts        bool                  `asn1:"optional,tag:2"`
    OnlySomeReasons            asn1.BitString        `asn1:"optional,tag:3"`
    IndirectCRL                bool                  `asn1:"optional,tag:4"`
    OnlyContainsAttributeCerts bool                  `asn1:"optional,tag:5"`
}

type distributionPointName struct {
    FullName     []asn1.RawValue  `asn1:"optional,tag:0"`
    RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
}

distributionPointName の FullName フィールドの型は GeneralNames です。
GeneralName の uniformResourceIdentifier で証明書失効リストの取得先を指定したいので、
asn1.RawValue 型で以下のように設定
Class: 2
Context-specific ( asn1.RawValue の定義による)
Tag: 6
GeneralName の6番目つまり uniformResourceIdentifier
Bytes: []byte("http://www.example.com/example.crl")
uniformResourceIdentifier の エンコーディングは IA5String です。ただ、crl への URI で使われている範囲の文字列は IA5String と UTF8 で同じbyteになるので直接 byte 配列として渡しています。

    dp := distributionPointName{
        FullName: []asn1.RawValue{
            {Tag: 6, Class: 2, Bytes: []byte("http://www.example.com/example.crl")},
        },
    }

Extension に作成した IssuingDistributionPoint を設定します。

var oidExtensionIssuingDistributionPoint = []int{2, 5, 29, 28}

    idp := issuingDistributionPoint{
        DistributionPoint: dp,
    }

    v, err := asn1.Marshal(idp)

    cdpExt := pkix.Extension{
        Id:       oidExtensionIssuingDistributionPoint,
        Critical: true,
        Value:    v,
    }

x509.RevocationList 構造体の設定

x509.RevocationList 構造体に設定したい値を入れていきます。

    crlTpl := &x509.RevocationList{
        SignatureAlgorithm:  x509.SHA256WithRSA,
        RevokedCertificates: rcs,
        Number:              big.NewInt(2),
        ThisUpdate:          time.Now(),
        NextUpdate:          time.Now().Add(24 * time.Hour),
        ExtraExtensions:     []pkix.Extension{cdpExt},
    }

証明書失効リストを作成

証明書失効リストを発行します

    var derCrl []byte
    derCrl, err = x509.CreateRevocationList(rand.Reader, crlTpl, derCaCert, privateCaKey)
    if err != nil {
        log.Fatalf("ERROR:%v\n", err)
    }

    f, err = os.Create("ca01.crl")
    if err != nil {
        log.Fatalf("ERROR:%v\n", err)
    }

    err = pem.Encode(f, &pem.Block{Type: "X509 CRL", Bytes: derCrl})
    if err != nil {
        log.Fatalf("ERROR:%v\n", err)
    }
    err = f.Close()

5. 証明書失効リストを確認する

発行した証明書失効リストを Openssl で確認します。設定した要素がすべて入っていますね。

$ openssl crl -inform pem -in example.crl -text
Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = JP, O = Example Org, OU = Example Org Unit, CN = ca01
        Last Update: Oct 24 04:16:04 2020 GMT
        Next Update: Oct 25 04:16:04 2020 GMT
        CRL extensions:
            X509v3 Authority Key Identifier:
                keyid:0A:42:8D:9B:23:A9:77:11:FF:FD:0F:CC:58:F4:36:F4:98:06:7F:28

            X509v3 CRL Number:
                2
            X509v3 Issuing Distribution Point: critical
                Full Name:
                  URI:http://www.example.com/example.crl

Revoked Certificates:
    Serial Number: 64
        Revocation Date: Oct 24 04:16:04 2020 GMT
    Serial Number: 6C
        Revocation Date: Oct 24 04:16:04 2020 GMT
    Signature Algorithm: sha256WithRSAEncryption
         6c:0d:23:e8:50:bf:84:ae:10:85:3e:43:28:0f:43:fd:58:cb:
         83:8c:7c:a8:5c:7d:78:71:f1:0c:03:97:43:88:8c:32:02:5c:
         a6:6c:e2:a4:7d:94:56:08:a8:9c:17:95:b4:be:11:bb:65:52:
         43:25:de:c0:d5:d0:df:ac:0f:ca:8c:a7:23:82:19:12:e2:9d:
         49:83:9e:ca:bc:2e:f3:60:79:39:47:cb:ed:17:52:25:9f:42:
         26:9e:1b:67:5f:af:e1:3a:14:67:5f:4f:de:10:c5:32:03:7f:
         40:a0:b6:bc:3f:05:33:73:91:0b:73:4e:f2:3c:be:b0:e4:63:
         e0:d0:81:6e:91:14:d9:04:35:21:3e:22:1e:31:bd:47:40:c9:
         69:f0:e5:57:bc:c3:2c:ae:b8:06:38:35:f1:59:6f:45:2c:45:
         08:2e:63:49:ab:f5:54:0b:54:d2:a8:fc:62:ea:a5:46:62:28:
         a9:89:76:96:cf:47:28:3d:81:c3:e9:fb:ce:54:a8:07:71:6d:
         c6:d8:b7:e7:33:b0:05:df:c4:79:56:e1:99:ed:9f:33:f8:15:
         b9:32:4e:82:4c:0c:a7:a5:23:d4:f7:e1:94:26:2b:e0:55:1a:
         38:f6:72:21:a9:e0:29:06:80:9a:05:e3:43:c2:4a:dd:74:c6:
         d6:79:ec:9d
-----BEGIN X509 CRL-----
MIICKDCCARACAQEwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCSlAxFDASBgNV
BAoTC0V4YW1wbGUgT3JnMRkwFwYDVQQLExBFeGFtcGxlIE9yZyBVbml0MQ0wCwYD
VQQDEwRjYTAxFw0yMDEwMjQwNDE2MDRaFw0yMDEwMjUwNDE2MDRaMCgwEgIBZBcN
MjAxMDI0MDQxNjA0WjASAgFsFw0yMDEwMjQwNDE2MDRaoGUwYzAfBgNVHSMEGDAW
gBQKQo2bI6l3Ef/9D8xY9Db0mAZ/KDAKBgNVHRQEAwIBAjA0BgNVHRwBAf8EKjAo
oCagJIYiaHR0cDovL3d3dy5leGFtcGxlLmNvbS9leGFtcGxlLmNybDANBgkqhkiG
9w0BAQsFAAOCAQEAbA0j6FC/hK4QhT5DKA9D/VjLg4x8qFx9eHHxDAOXQ4iMMgJc
pmzipH2UVgionBeVtL4Ru2VSQyXewNXQ36wPyoynI4IZEuKdSYOeyrwu82B5OUfL
7RdSJZ9CJp4bZ1+v4ToUZ19P3hDFMgN/QKC2vD8FM3ORC3NO8jy+sORj4NCBbpEU
2QQ1IT4iHjG9R0DJafDlV7zDLK64Bjg18VlvRSxFCC5jSav1VAtU0qj8YuqlRmIo
qYl2ls9HKD2Bw+n7zlSoB3Ftxti35zOwBd/EeVbhme2fM/gVuTJOgkwMp6Uj1Pfh
lCYr4FUaOPZyIangKQaAmgXjQ8JK3XTG1nnsnQ==
-----END X509 CRL-----

6. コード

コードはこちら
https://github.com/tardevnull/gopkicookbook4

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Golang】Alpine Docker で実行すると error: stdlib.h: No such file or directory. "stdlib.h" が足りないと言われる

golang:alpine の Docker イメージで go rungo test を実行すると stdlib.h: No such file or directoryfatal エラーで叱られる。

「"golang" "alpine" fatal error: stdlib.h: No such file or directory」とググっても日本語で情報が出てこなかったので。

$ go test ./...
Testing main package
go: downloading ...
...
# runtime/cgo
exec: "gcc": executable file not found in $PATH
  FAIL  github.com/KEINOS/Sample [build failed]
  FAIL  github.com/KEINOS/Sample/hoge [build failed]
  • go version: go version go1.15.3 linux/amd64
  • uname -a: Linux a7325e2dcf0f 4.19.76-linuxkit #1 SMP Tue May 26 11:42:35 UTC 2020 x86_64 Linux
  • cat /etc/os-release | grep PRETTY_NAME: PRETTY_NAME="Alpine Linux v3.12"

TL; DR

ミニマル・バージョン
# Minimum, at-least-to-install packages to run/build
apk add --no-cache gcc musl-dev
横着バージョン(上記も含まれています)
# Base meta-package to run/build (This includes the above packages as well)
apk add --no-cache build-base
Alpine上で開発するなら、とりま入れておけパック
# For Dev, better to be installed packages
apk add --no-cache alpine-sdk build-base

TS; DR(上記に至るまで勉強したこと)

とあるマシンで Go言語(以下 Golang)のコードを触ってコンパイる必要があったのですが、Golang がインストールされていませんでした。しかも別途インストールすることが許されず。。。

しかし、Docker はインストールされていたので golang:alpine のイメージで試そうと思いました。

ローカルのカレントディレクトリをコンテナの/goディレクトリにマウントしながら起動
$ # Docker でマウントを取ってみる
$ cd /path/to/the/repo
$ docker run --rm -it -v $(pwd):/go golang:alpine /bin/sh
...
/go #

$GOPATH/go.mod exists but should not エラー

まずはテストの実行です。動きません。

/go # go test main.go ./...
$GOPATH/go.mod exists but should not

/go # go mod download
$GOPATH/go.mod exists but should not
/go # go mod verify
$GOPATH/go.mod exists but should not
/go # go env
$GOPATH/go.mod exists but should not

/go # env
HOSTNAME=xxxxxxxxxxxx
SHLVL=1
HOME=/root
TERM=xterm
PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
GOPATH=/go
PWD=/go
GOLANG_VERSION=1.15.3

mod がインストールされていないからか、と思えば違うようです。どうやら上記エラーは GOPATH モードとモジュール・モードに関係しているようです。

確かに環境変数に GO111MODULE がありませんでした。そのため GOPATH モードになっているからなのかと思い GO111MODULE=on で「常にモジュール対応モードで動作する」ように設定して見たのですが、残念ながらダメでした。

GO111MODULE=onで起動
$ docker run --rm -it -v $(pwd):/go -e GO111MODULE=on golang:alpine /bin/sh
/go #
/go # echo $GO111MODULE
on
/go # go env
$GOPATH/go.mod exists but should not

GO v1.15 なので、そもそもデフォルトで GO111MODULEauto になっているようで、以下のリンクによれば回避策は2つ。

  1. /go 以外の別のディレクトリにソースコードをマウントして実行する。
  2. export GOPATH= と環境変数の設定値を「空」にセットしてユーザーディレクトリ下にキャッシュさせる。

原因はモジュール・モードなのに GOPATH で指定されたディレクトリに開発用の mod.go 付きのソースコードを置いたことでした。

モジュール・モードの場合は GOPATH で指定されたディレクトリの下にモジュールをキャッシュして行きます。そのため、そのキャッシュ・ディレクトリに go.mod などの余計なファイルがあったことがエラーの原因でした。確かに「$GOPATH/go.mod exists but should not」と「$GOPATH/go.mod があるけど禁則事項です」と言ってます。

とりあえず、/go でなく /app にマウントすることにしました。

ローカルのカレントディレクトリを/appにマウントしながら起動
$ cd /path/to/the/repo
$ docker run --rm -it -v $(pwd):/app golang:alpine /bin/sh
/go #
/go # # マウントしたディレクトリに移動
/go # cd /app
/app #
/app # # 今度は go env が表示された。GO111MODULEは空なので auto ∴ モジュールモード
/app # go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build030913204=/tmp/go-build -gno-record-gcc-switches"

"gcc": executable file not found in $PATH エラー

そして気を取り直して、再テストです。先へ進んだものの、動きません。

テストの実行
/app # go test ./...
go: downloading github.com/...
go: downloading github.com/...
...
# runtime/cgo
exec: "gcc": executable file not found in $PATH
FAIL    github.com/KEINOS/Hello-Cobra [build failed]
FAIL    github.com/KEINOS/Hello-Cobra/hoge [build failed]

なんか cgo が怒っています。gcc がない、と。えー cgo 使ってないんだけどなぁ。依存パッケージが使ってるのかしら。

とりあえず gcc をインストールして再度テストして見ます。

gccのインストール
/app # # gcc 入ってない
/app # gcc --version
/bin/sh: gcc: not found
/app #
/app # # gcc 入れる
/app # apk add --no-cache gcc
...
/app # # gcc 入った
/app # gcc --version
gcc (Alpine 9.3.0) 9.3.0
再テスト
/app # go test ./...
# runtime/cgo
_cgo_export.c:3:10: fatal error: stdlib.h: No such file or directory
    3 | #include <stdlib.h>
      |          ^~~~~~~~~~
compilation terminated.
FAIL    github.com/KEINOS/Hello-Cobra [build failed]
FAIL    github.com/KEINOS/Hello-Cobra/hoge [build failed]

やはり、どこかで cgo を使っているらしく、叱られます。今度は stdlib.h が足りないら C です。どうやら依存パッケージが cgo を使っているぽいです。

GitHub にある Golang の Docker のリポジトリに Issue が上がってました。Alpine の cgomusl-dev が必要らしいです。そういや、Alpine 系の Issue で良く見る。

try adding musl-dev
Not sure why gcc doesnt depend on it.
CGO does not seem to work on golang:1.6-alpine #86 @ GitHub より)

それでは、再度テストをしてみます。動きました。

/app # # musl-dev 入れる
/app # apk add --no-cache musl-dev
...
/app # # テストする
/app # go test ./...
ok      github.com/KEINOS/Sample        0.003s
ok      github.com/KEINOS/Sample/hoge   0.006s
/app # # ? 

Yeah, the alpine images are designed to be minimal.
CGO does not seem to work on golang:1.6-alpine #86 @ GitHub より)

「そうさ。Alpine イメージは必要最低限になるように設計されてるのさ」と、ドキュメントを嫁と言ってます。

The main caveat to note is that it does use musl libc instead of glibc and friends, which can lead to unexpected behavior.
...
To minimize image size, additional related tools (such as git, gcc, or bash) are not included in Alpine-based images. Using this image as a base, add the things you need in your own Dockerfile (see the alpine image description for examples of how to install packages if you are unfamiliar).
golang:-alpine | Quick reference | Golang | docker-library @ GitHub より)

「何はともあれ忠告しておくと、Alpine は、glibc とその愉快な仲間たちの代わりに、挙動が読めない musl libc を使っているからね。」と、あります。そして、サイズを極力小さくするため git とか gcc とか bash すら入れていない、と。

そういえば、そうでした。Alpine はコンテナ向けなので何も入っていないので、Dockerfile で必要な物を入れてあげて使う物だったのを失念しておりました。

「なんか、あれこれ apk パッケージを探してインストールするのも面倒だなぁ」と Ubuntu のとりあえず入れておけパック「build-essential」みたいなものがないかググったら、「ビルドによく使われるものを集めたパッケージ」と「開発によく使われるものを集めたパッケージ」があるらしい。

ビルドに必要な良くあるものパック
apk add build-base
開発に必要な良くあるものパック
apk add alpine-sdk

確かに、gccmusl-dev を入れなくても build-base でいけた。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む