aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/rosa/all.go
blob: 6f0aefccd16d41fc394ae2e250669fc9646abbea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
package rosa

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"sync"

	"hakurei.app/internal/pkg"
)

// PArtifact is a lazily-initialised [pkg.Artifact] preset.
type PArtifact int

const (
	LLVM PArtifact = iota

	// EarlyInit is the Rosa OS init program.
	EarlyInit
	// ImageSystem is the Rosa OS /system image.
	ImageSystem
	// ImageInitramfs is the Rosa OS initramfs archive.
	ImageInitramfs

	// Kernel is the generic Rosa OS Linux kernel.
	Kernel
	// KernelHeaders is an installation of kernel headers for [Kernel].
	KernelHeaders
	// KernelSource is a writable kernel source tree installed to [AbsUsrSrc].
	KernelSource
	// Firmware is firmware blobs for use with the Linux kernel.
	Firmware

	ACL
	ArgpStandalone
	Attr
	Autoconf
	Automake
	BC
	Bash
	Binutils
	Bison
	Bzip2
	CMake
	Connman
	Coreutils
	Curl
	DBus
	DTC
	Diffutils
	Elfutils
	Fakeroot
	Findutils
	Flex
	Fuse
	GMP
	GLib
	Gawk
	GenInitCPIO
	Gettext
	Git
	Glslang
	GnuTLS
	Go
	Gperf
	Grep
	Gzip
	Hakurei
	HakureiDist
	IPTables
	Kmod
	LibXau
	Libbsd
	Libcap
	Libdrm
	Libev
	Libexpat
	Libffi
	Libgd
	Libglvnd
	Libiconv
	Libmd
	Libmnl
	Libpciaccess
	Libnftnl
	Libpsl
	Libseccomp
	Libtasn1
	Libtool
	Libucontext
	Libunistring
	Libxml2
	Libxslt
	M4
	MPC
	MPFR
	Make
	Meson
	Mksh
	MuslFts
	MuslObstack
	NSS
	NSSCACert
	Ncurses
	Nettle
	Ninja
	OpenSSL
	P11Kit
	PCRE2
	Parallel
	Patch
	Perl
	PerlLocaleGettext
	PerlMIMECharset
	PerlModuleBuild
	PerlPodParser
	PerlSGMLS
	PerlTermReadKey
	PerlTextCharWidth
	PerlTextWrapI18N
	PerlUnicodeLineBreak
	PerlYAMLTiny
	PkgConfig
	Procps
	Python
	PythonFlitCore
	PythonHatchling
	PythonIniConfig
	PythonMako
	PythonMarkupSafe
	PythonPackaging
	PythonPathspec
	PythonPluggy
	PythonPyTest
	PythonPyYAML
	PythonPygments
	PythonSetuptools
	PythonSetuptoolsSCM
	PythonTroveClassifiers
	PythonVCSVersioning
	PythonWheel
	QEMU
	Rdfind
	Readline
	Rsync
	Sed
	SPIRVHeaders
	SPIRVTools
	SquashfsTools
	Strace
	TamaGo
	Tar
	Texinfo
	Toybox
	toyboxEarly
	Unzip
	UtilLinux
	Wayland
	WaylandProtocols
	XCB
	XCBProto
	XDGDBusProxy
	XZ
	Xproto
	Zlib
	Zstd

	// PresetUnexportedStart is the first unexported preset.
	PresetUnexportedStart

	llvmSource = iota - 1
	buildcatrust
	utilMacros

	// Musl is a standalone libc that does not depend on the toolchain.
	Musl
	// muslHeaders is a system installation of [Musl] headers.
	muslHeaders

	// gcc is a hacked-to-pieces GCC toolchain meant for use in intermediate
	// stages only. This preset and its direct output must never be exposed.
	gcc

	// nettle3 is an older version of [Nettle].
	nettle3

	// Stage0 is a tarball containing all compile-time dependencies of artifacts
	// part of the [Std] toolchain.
	Stage0

	// PresetEnd is the total number of presets and does not denote a preset.
	PresetEnd
)

// P represents multiple [PArtifact] and is stable through JSON.
type P []PArtifact

// MarshalJSON represents [PArtifact] by their [Metadata.Name].
func (s P) MarshalJSON() ([]byte, error) {
	names := make([]string, len(s))
	for i, p := range s {
		names[i] = GetMetadata(p).Name
	}
	return json.Marshal(names)
}

// UnmarshalJSON resolves the value created by MarshalJSON back to [P].
func (s *P) UnmarshalJSON(data []byte) error {
	var names []string
	if err := json.Unmarshal(data, &names); err != nil {
		return err
	}

	*s = make(P, len(names))
	for i, name := range names {
		if p, ok := ResolveName(name); !ok {
			return fmt.Errorf("unknown artifact %q", name)
		} else {
			(*s)[i] = p
		}
	}
	return nil
}

// Metadata is stage-agnostic information of a [PArtifact] not directly
// representable in the resulting [pkg.Artifact].
type Metadata struct {
	f func(t Toolchain) (a pkg.Artifact, version string)

	// Unique package name.
	Name string `json:"name"`
	// Short user-facing description.
	Description string `json:"description"`
	// Project home page.
	Website string `json:"website,omitempty"`

	// Runtime dependencies.
	Dependencies P `json:"dependencies"`

	// Project identifier on [Anitya].
	//
	// [Anitya]: https://release-monitoring.org/
	ID int `json:"-"`

	// Optional custom version checking behaviour.
	latest func(v *Versions) string
}

// GetLatest returns the latest version described by v.
func (meta *Metadata) GetLatest(v *Versions) string {
	if meta.latest != nil {
		return meta.latest(v)
	}
	return v.Latest
}

// Unversioned denotes an unversioned [PArtifact].
const Unversioned = "\x00"

// UnpopulatedIDError is returned by [Metadata.GetLatest] for an instance of
// [Metadata] where ID is not populated.
type UnpopulatedIDError struct{}

func (UnpopulatedIDError) Unwrap() error { return errors.ErrUnsupported }
func (UnpopulatedIDError) Error() string { return "Anitya ID is not populated" }

// Versions are package versions returned by Anitya.
type Versions struct {
	// The latest version for the project, as determined by the version sorting algorithm.
	Latest string `json:"latest_version"`
	// List of all versions that aren’t flagged as pre-release.
	Stable []string `json:"stable_versions"`
	// List of all versions stored, sorted from newest to oldest.
	All []string `json:"versions"`
}

// getStable returns the first Stable version, or Latest if that is unavailable.
func (v *Versions) getStable() string {
	if len(v.Stable) == 0 {
		return v.Latest
	}
	return v.Stable[0]
}

// GetVersions returns versions fetched from Anitya.
func (meta *Metadata) GetVersions(ctx context.Context) (*Versions, error) {
	if meta.ID == 0 {
		return nil, UnpopulatedIDError{}
	}

	var resp *http.Response
	if req, err := http.NewRequestWithContext(
		ctx,
		http.MethodGet,
		"https://release-monitoring.org/api/v2/versions/?project_id="+
			strconv.Itoa(meta.ID),
		nil,
	); err != nil {
		return nil, err
	} else {
		req.Header.Set("User-Agent", "Rosa/1.1")
		if resp, err = http.DefaultClient.Do(req); err != nil {
			return nil, err
		}
	}

	var v Versions
	err := json.NewDecoder(resp.Body).Decode(&v)
	return &v, errors.Join(err, resp.Body.Close())
}

var (
	// artifactsM is an array of [PArtifact] metadata.
	artifactsM [PresetEnd]Metadata

	// artifacts stores the result of Metadata.f.
	artifacts [_toolchainEnd][len(artifactsM)]struct {
		a pkg.Artifact
		v string
	}
	// artifactsOnce is for lazy initialisation of artifacts.
	artifactsOnce [_toolchainEnd][len(artifactsM)]sync.Once

	// presetOpts globally modifies behaviour of presets.
	presetOpts int
)

const (
	// OptSkipCheck skips running all test suites.
	OptSkipCheck = 1 << iota
	// OptLLVMNoLTO disables LTO in all [LLVM] stages.
	OptLLVMNoLTO
)

// Flags returns the current preset flags
func Flags() int { return presetOpts }

// zero zeros the value pointed to by p.
func zero[T any](p *T) { var v T; *p = v }

// DropCaches arranges for all cached [pkg.Artifact] to be freed some time after
// it returns. Must not be used concurrently with any other function from this
// package.
func DropCaches(flags int) {
	presetOpts = flags
	zero(&artifacts)
	zero(&artifactsOnce)
}

// GetMetadata returns [Metadata] of a [PArtifact].
func GetMetadata(p PArtifact) *Metadata { return &artifactsM[p] }

// construct constructs a [pkg.Artifact] corresponding to a [PArtifact] once.
func (t Toolchain) construct(p PArtifact) {
	artifactsOnce[t][p].Do(func() {
		artifacts[t][p].a, artifacts[t][p].v = artifactsM[p].f(t)
	})
}

// Load returns the resulting [pkg.Artifact] of [PArtifact].
func (t Toolchain) Load(p PArtifact) pkg.Artifact {
	t.construct(p)
	return artifacts[t][p].a
}

// Version returns the version string of [PArtifact].
func (t Toolchain) Version(p PArtifact) string {
	t.construct(p)
	return artifacts[t][p].v
}

// ResolveName returns a [PArtifact] by name.
func ResolveName(name string) (p PArtifact, ok bool) {
	for i := range PresetUnexportedStart {
		if name == artifactsM[i].Name {
			return i, true
		}
	}
	return 0, false
}