-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmain.dang
More file actions
228 lines (212 loc) · 9.02 KB
/
Copy pathmain.dang
File metadata and controls
228 lines (212 loc) · 9.02 KB
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
"""
Manage Dagger modules that use the Java SDK (new self-contained code organisation).
Modules created by this SDK use the self-contained layout: the Java SDK is
vendored as source and code is generated into the module, and the module runtime
is this repository's build/package-only runtime
(`github.com/dagger/java-sdk/runtime`). Because the generated files are committed,
the runtime skips codegen at module load and just builds and packages the
committed sources.
"""
type JavaSdk {
"""
Starter under templates/<template> for a module generated without source: default*, empty, legacy
"""
template: String! = "default"
"""
Commit the compiled Dagger Java SDK as a jar into each generated module so its
runtime build compiles only the module's own code against the jar instead of
recompiling the vendored SDK sources. The sources stay checked in for IDE use.
Opt-in (set `settings.vendorSdkJar = true` under `[modules.java-sdk]` in the
workspace dagger.toml) because it adds a committed binary artifact.
"""
vendorSdkJar: Boolean! = false
"""
Record the commit each git client resolved to as a pin in the module's
manifest, so a generated module resolves the same dependencies later. Off by
default because a pinned client stops following the branch or tag it was added
at until the pin is refreshed.
"""
lock: Boolean! = false
"""
Runtime source written into the dagger-module.toml of new Java modules.
"""
let targetRuntime: String! = "github.com/dagger/java-sdk/runtime"
"""
The Java client root containing the workspace cwd: the directory of the
nearest pom.xml, relative to the workspace root. Every Java module has one at
its root, and the SDK vendored under a module carries none of its own, so the
nearest hit is always the owning module. Null when there is none — a project
built with anything but Maven is not a root this SDK can serve.
"""
findClientRoot(ws: Workspace!): String {
let found = ws.findUp("pom.xml")
if (found == null) {
null
} else {
normalizePath(found.trimSuffix("pom.xml"))
}
}
"""
Generate one SDK scope: the module at the workspace cwd, when the scope has
one. A scope with no config is scaffolded from the configured template first.
Every module then gets a dagger-module.toml from the manifest builder and is
generated; a pre-1.0 dagger.json is migrated into it and removed, so two
manifest files cannot disagree. The scope's module clients become the module's
dependencies, so the generated bindings include their types. Standalone
clients, in a scope without a module, are not generated yet.
"""
generateScope(
ws: Workspace!,
isModule: Boolean!,
name: String!,
clients: [ModuleSource!]!,
): Workspace! {
if (isModule == false) {
if (clients.length > 0) {
raise "java-sdk does not generate standalone module clients yet"
} else {
ws
}
} else {
let scope = normalizePath(ws.cwd)
# The rest works with the cwd at the workspace root: the engine resolves a
# module's local dependencies to workspace-root-relative paths and then
# reads them relative to Workspace.cwd (dagger/dagger ResolveDepToSource),
# so at the scope's cwd a dependency is looked up under the module itself.
# The scope cwd is restored on the way out because the engine rejects a
# generateScope result whose cwd is not the scope.
let rooted = ws.withWorkdir(".")
let initialized = if (hasModuleConfig(ws, scope)) {
rooted
} else {
rooted.withDirectory(scopeRef(scope), moduleTemplate(name))
}
let configured = scopeManifest(initialized, scope, name, clients)
Mod(
rootPath: scope,
ws: configured,
vendorSdkJar: vendorSdkJar,
).generated.withWorkdir(scope)
}
}
"""
Write the module's dagger-module.toml from the manifest it already has and the
complete client set, and drop a pre-1.0 dagger.json once its contents have
moved across. Fields this SDK does not own are carried over by loading the
existing manifest rather than building one from nothing.
"""
let scopeManifest(
ws: Workspace!,
scope: String!,
name: String!,
clients: [ModuleSource!]!,
): Workspace! {
let base = if (scopeHasFile(ws, scope, "dagger-module.toml")) {
sdkHelpers.moduleManifest(loadToml: ws.file(scopeRef(scope, "dagger-module.toml")))
} else if (scopeHasFile(ws, scope, "dagger.json")) {
sdkHelpers.moduleManifest(loadJson: ws.file(scopeRef(scope, "dagger.json")))
} else {
sdkHelpers.moduleManifest(loadToml: seedManifestFile(name))
}
# Cleared structurally rather than by name: withoutLegacyRuntimeDependency
# matches an unnamed dependency on its source, not on the module name it
# resolves to, and resolving the recorded dependencies to read their names
# would fail generation on one stale entry instead of dropping it.
let configured = clients
.reduce(base.withName(name: name).withoutLegacyRuntimeDependencies) { manifest, client =>
manifest.withLegacyRuntimeDependency(module: client)
}
# The builder records a local client relative to ws.cwd, so it runs from the
# scope while the caller keeps working from the workspace root. legacyJson is
# off because a module of this SDK is configured by its dagger-module.toml
# alone — the builder deletes the dagger.json one was migrated from.
configured.generate(ws.withWorkdir(scope), lock: lock, legacyJson: false).withWorkdir(".")
}
"""
The dagger-module.toml a new module starts from.
The builder has one runtime setter per builtin runtime, and this SDK targets
its own repository's runtime, so the runtime is named by loading a seed file:
the builder accepts a non-builtin runtime on a manifest loaded from a config
file and rejects it on one built from nothing. The caller loads this, so what
finally lands is the builder's own rendering, not these bytes.
"""
let seedManifestFile(name: String!): File! {
let seed = "name = \"" + name + "\"\n"
+ "engineVersion = \"" + engineVersion + "\"\n"
+ "\n"
+ "[runtime]\n"
+ " source = \"" + targetRuntime + "\"\n"
directory.withNewFile("dagger-module.toml", seed).file("dagger-module.toml")
}
"""
The live engine version, without the build metadata that changes on every
engine build.
"""
let engineVersion: String! { version.split("+")[0] ?? version }
"""
The files of a new module: the configured template rendered for the module
name. An empty template name selects the default.
"""
let moduleTemplate(name: String!): Directory! {
let selected = if (template == "") { "default" } else { template }
if (currentModule.source.exists("templates/" + selected) == false) {
raise "unknown template: " + template
} else {
renderedTemplate(name, selected)
}
}
"""
Render a Java template, substituting the requested module name.
"""
let renderedTemplate(name: String!, template: String!): Directory! {
container
.from("golang:1.25-alpine")
.withoutEntrypoint
.withMountedCache("/go/pkg/mod", cacheVolume("go-mod"))
.withMountedCache("/root/.cache/go-build", cacheVolume("go-build"))
.withDirectory(
"/helper",
currentModule.source.directory("helpers/render-java-template"),
)
# target/ is gitignored, so it is absent from a clean checkout but present
# in a developer's — and the renderer substitutes the module name into
# every file it walks, including .class bytes, producing a corrupt class
# that then breaks the scaffolded module's build.
.withDirectory(
"/template",
currentModule.source.directory("templates/" + template),
exclude: ["**/target/**"],
)
.withWorkdir("/helper")
.withExec(["go", "build", "-o", "/usr/local/bin/render-java-template", "."])
.withExec(["render-java-template", name, "/template", "/rendered"])
.directory("/rendered")
}
"""
Whether a module config, dagger-module.toml or the pre-1.0 dagger.json, exists
at a workspace-root-relative scope.
"""
let hasModuleConfig(ws: Workspace!, scope: String!): Boolean! {
scopeHasFile(ws, scope, "dagger-module.toml") or scopeHasFile(ws, scope, "dagger.json")
}
let scopeHasFile(ws: Workspace!, scope: String!, filename: String!): Boolean! {
let path = if (scope == ".") { filename } else { scope + "/" + filename }
ws.directory("/", include: [path]).exists(path)
}
"""
A scope path, or a file under it, as a workspace-root-absolute path — the form
Workspace resolves from the workspace root rather than from the client's cwd.
"""
let scopeRef(scope: String!, sub: String! = ""): String! {
let root = if (scope == ".") { "" } else { "/" + scope }
if (sub == "") {
if (root == "") { "/" } else { root }
} else {
root + "/" + sub
}
}
let normalizePath(path: String!): String! {
let normalized = path.trimPrefix("./").trimPrefix("/").trimSuffix("/")
if (normalized == "") { "." } else { normalized }
}
}