Skip to content

Commit 1261271

Browse files
Support sphinx integration configuration
This updates the sphinx integration to have a built-in configuration object that it pulls various settings from. When Smithy 1.41 is released, these will be easily configurable from the smithy-build.json configuration. This also slightly changes the doc settings's node parsing function so it can be discovered by Smithy's NodeMapper, which lets us use directed codegen's automatic settings mapping, which in turn will automatically handle passing along integration settings when Smithy 1.41 is released. One more change will be needed when 1.41 is released: the configure method will need to be implemented to actually properly set the settings.
1 parent a1ee495 commit 1261271

File tree

3 files changed

+139
-19
lines changed

3 files changed

+139
-19
lines changed

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/DocSettings.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public record DocSettings(ShapeId service, String format) {
3838
* @param pluginSettings the {@code ObjectNode} to load settings from.
3939
* @return loaded settings based on the given node.
4040
*/
41-
public static DocSettings from(ObjectNode pluginSettings) {
41+
public static DocSettings fromNode(ObjectNode pluginSettings) {
4242
return new DocSettings(
4343
pluginSettings.expectStringMember("service").expectShapeId(),
4444
pluginSettings.getStringMemberOrDefault("format", "sphinx-markdown")

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/SmithyDocPlugin.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,14 @@ public String getName() {
3939
@Override
4040
public void execute(PluginContext pluginContext) {
4141
LOGGER.fine("Beginning documentation generation.");
42-
DocSettings settings = DocSettings.from(pluginContext.getSettings());
43-
4442
CodegenDirector<DocWriter, DocIntegration, DocGenerationContext, DocSettings> runner
4543
= new CodegenDirector<>();
4644

4745
runner.directedCodegen(new DirectedDocGen());
4846
runner.integrationClass(DocIntegration.class);
4947
runner.fileManifest(pluginContext.getFileManifest());
5048
runner.model(getValidatedModel(pluginContext.getModel()).unwrap());
51-
runner.settings(settings);
49+
DocSettings settings = runner.settings(DocSettings.class, pluginContext.getSettings());
5250
runner.service(settings.service());
5351
runner.performDefaultCodegenTransforms();
5452
runner.run();

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/integrations/SphinxIntegration.java

Lines changed: 137 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static software.amazon.smithy.docgen.core.DocgenUtils.normalizeNewlines;
1010
import static software.amazon.smithy.docgen.core.DocgenUtils.runCommand;
1111

12+
import java.util.ArrayList;
1213
import java.util.List;
1314
import java.util.Set;
1415
import java.util.logging.Logger;
@@ -22,6 +23,9 @@
2223
import software.amazon.smithy.docgen.core.sections.sphinx.RequirementsSection;
2324
import software.amazon.smithy.docgen.core.sections.sphinx.WindowsMakeSection;
2425
import software.amazon.smithy.docgen.core.writers.SphinxMarkdownWriter;
26+
import software.amazon.smithy.model.node.Node;
27+
import software.amazon.smithy.model.node.ObjectNode;
28+
import software.amazon.smithy.model.node.StringNode;
2529
import software.amazon.smithy.model.shapes.ServiceShape;
2630
import software.amazon.smithy.utils.SmithyInternalApi;
2731

@@ -47,6 +51,32 @@
4751
* build the docs. Any dependencies here will be installed into the environment
4852
* used to run {@code sphinx-build}.
4953
* </ul>
54+
*
55+
* This integration supports several customization options. To see all those options,
56+
* see {@link SphinxSettings}. These settings are configured similarly to the doc
57+
* generation plugin settings. Below is an example {@code smithy-build.json} with
58+
* sphinx project auto build disabled.
59+
*
60+
* <pre>{@code
61+
* {
62+
* "version": "1.0",
63+
* "projections": {
64+
* "sphinx-markdown": {
65+
* "plugins": {
66+
* "docgen": {
67+
* "service": "com.example#DocumentedService",
68+
* "format": "sphinx-markdown",
69+
* "integrations": {
70+
* "sphinx": {
71+
* "autoBuild": false
72+
* }
73+
* }
74+
* }
75+
* }
76+
* }
77+
* }
78+
* }
79+
* }</pre>
5080
*/
5181
@SmithyInternalApi
5282
public final class SphinxIntegration implements DocIntegration {
@@ -55,12 +85,19 @@ public final class SphinxIntegration implements DocIntegration {
5585
private static final Logger LOGGER = Logger.getLogger(SphinxIntegration.class.getName());
5686

5787
// The default requirements needed to build the docs.
58-
private static final List<String> REQUIREMENTS = List.of(
88+
private static final List<String> BASE_REQUIREMENTS = List.of(
5989
"Sphinx==7.2.6",
6090
"myst-parser==2.0.0",
6191
"linkify-it-py==2.0.2"
6292
);
6393

94+
private SphinxSettings settings = SphinxSettings.fromNode(Node.objectNode());
95+
96+
@Override
97+
public String name() {
98+
return "sphinx";
99+
}
100+
64101
@Override
65102
public byte priority() {
66103
// Run at the end so that any integration-generated changes can happen.
@@ -83,7 +120,6 @@ public void customize(DocGenerationContext context) {
83120
));
84121
return;
85122
}
86-
// TODO: add some way to disable project file generation
87123
LOGGER.info("Generating Sphinx project files.");
88124
writeRequirements(context);
89125
writeConf(context);
@@ -93,8 +129,13 @@ public void customize(DocGenerationContext context) {
93129

94130
private void writeRequirements(DocGenerationContext context) {
95131
context.writerDelegator().useFileWriter("requirements.txt", writer -> {
96-
writer.pushState(new RequirementsSection(context, REQUIREMENTS));
97-
REQUIREMENTS.forEach(writer::write);
132+
// Merge base and configured requirements into a single immutable list
133+
List<String> requirements = new ArrayList<>(BASE_REQUIREMENTS);
134+
requirements.addAll(settings.extraDependencies());
135+
requirements = List.copyOf(requirements);
136+
137+
writer.pushState(new RequirementsSection(context, requirements));
138+
requirements.forEach(writer::write);
98139
writer.popState();
99140
});
100141
}
@@ -114,10 +155,11 @@ private void writeConf(DocGenerationContext context) {
114155
release = $2S
115156
templates_path = ["_templates"]
116157
html_static_path = ["_static"]
117-
html_theme = "alabaster"
158+
html_theme = $3S
118159
""",
119160
serviceSymbol.getName(),
120-
service.getVersion());
161+
service.getVersion(),
162+
settings.theme());
121163

122164
if (context.docFormat().name().equals(MARKDOWN_FORMAT)) {
123165
writer.write("""
@@ -209,6 +251,12 @@ private void writeMakefile(DocGenerationContext context) {
209251
}
210252

211253
private void runSphinx(DocGenerationContext context) {
254+
if (!settings.autoBuild()) {
255+
LOGGER.info("Auto-build has been disabled. Skipping sphinx-build.");
256+
logManualBuildInstructions(context);
257+
return;
258+
}
259+
212260
var baseDir = context.fileManifest().getBaseDir();
213261

214262
LOGGER.info("Flushing writers in preparation for sphinx-build.");
@@ -236,7 +284,7 @@ private void runSphinx(DocGenerationContext context) {
236284
runCommand("./venv/bin/pip install -r requirements.txt", baseDir);
237285

238286
// Finally, run sphinx itself.
239-
runCommand("./venv/bin/sphinx-build -M html content build", baseDir);
287+
runCommand("./venv/bin/sphinx-build -M " + settings.format() + " content build", baseDir);
240288

241289
System.out.printf(normalizeNewlines("""
242290
Successfully built HTML docs. They can be found in "%1$s".
@@ -247,21 +295,22 @@ private void runSphinx(DocGenerationContext context) {
247295
environment docs for information on how to activate it: \
248296
https://docs.python.org/3/library/venv.html#how-venvs-work
249297
250-
Once the environment is activated, run `make html` from "%3$s" to \
251-
to build the docs, substituting html for whatever format you wish \
298+
Once the environment is activated, run `make %4$s` from "%3$s" to \
299+
to build the docs, substituting %4$s for whatever format you wish \
252300
to build.
253301
254302
To build the docs without activating the virtual environment, simply \
255-
run `./venv/bin/sphinx-build -M html content build` from "%3$s", \
256-
similarly substituting html for your desired format.
303+
run `./venv/bin/sphinx-build -M %4$s content build` from "%3$s", \
304+
similarly substituting %4$s for your desired format.
257305
258306
See sphinx docs for other output formats you can choose: \
259307
https://www.sphinx-doc.org/en/master/usage/builders/index.html
260308
261309
"""),
262-
baseDir.resolve("build/html"),
310+
baseDir.resolve("build/" + settings.format()),
263311
baseDir.resolve("venv"),
264-
baseDir
312+
baseDir,
313+
settings.format()
265314
);
266315
} catch (CodegenException e) {
267316
LOGGER.warning("Unable to automatically build HTML docs: " + e);
@@ -279,13 +328,86 @@ private void logManualBuildInstructions(DocGenerationContext context) {
279328
instead install them from your system package manager, or another \
280329
source.
281330
282-
Once the dependencies are installed, run `make html` from \
331+
Once the dependencies are installed, run `make %2$s` from \
283332
"%1$s". Other output formats can also be built. See sphinx docs for \
284333
other output formats: \
285334
https://www.sphinx-doc.org/en/master/usage/builders/index.html
286335
287336
"""),
288-
context.fileManifest().getBaseDir()
337+
context.fileManifest().getBaseDir(),
338+
settings.format()
289339
);
290340
}
341+
342+
/**
343+
* Settings for sphinx projects, regardless of their intermediate format.
344+
*
345+
* <p>These settings can be set in the {@code smithy-build.json} file under the
346+
* {@code sphinx} key of the doc generation plugin's {@code integrations} config.
347+
* The following example shows a {@code smithy-build.json} configuration that sets
348+
* the default sphinx output format to be dirhtml instead of html.
349+
*
350+
* <pre>{@code
351+
* {
352+
* "version": "1.0",
353+
* "projections": {
354+
* "sphinx-markdown": {
355+
* "plugins": {
356+
* "docgen": {
357+
* "service": "com.example#DocumentedService",
358+
* "format": "sphinx-markdown",
359+
* "integrations": {
360+
* "sphinx": {
361+
* "format": "dirhtml"
362+
* }
363+
* }
364+
* }
365+
* }
366+
* }
367+
* }
368+
* }
369+
* }</pre>
370+
*
371+
* @param format The sphinx output format that will be built automatically during
372+
* generation. The default is html. See
373+
* <a href="https://www.sphinx-doc.org/en/master/usage/builders/index.html">
374+
* sphinx docs</a> for other output format options.
375+
* @param theme The sphinx html theme to use. The default is alabaster. If your
376+
* chosen theme requires a python dependency to be added, use the
377+
* {@link #extraDependencies} setting.
378+
* @param extraDependencies Any extra python dependencies that should be added to
379+
* the {@code requirements.txt} file for the sphinx project.
380+
* These can be particularly useful for custom {@link #theme}s.
381+
* @param autoBuild Whether to automatically attempt to build the generated sphinx
382+
* project. The default is true. This will attempt to discover Python
383+
* 3 on the path, create a virtual environment inside the output
384+
* directory, install all the dependencies into that virtual environment,
385+
* and finally run sphinx-build.
386+
*/
387+
public record SphinxSettings(
388+
String format,
389+
String theme,
390+
List<String> extraDependencies,
391+
boolean autoBuild
392+
) {
393+
/**
394+
* Load the settings from an {@code ObjectNode}.
395+
*
396+
* @param node the {@code ObjectNode} to load settings from.
397+
* @return loaded settings based on the given node.
398+
*/
399+
public static SphinxSettings fromNode(ObjectNode node) {
400+
List<String> extraDependencies = List.of();
401+
if (node.containsMember("extraDependencies")) {
402+
var array = node.expectArrayMember("extraDependencies");
403+
extraDependencies = array.getElementsAs(StringNode::getValue);
404+
}
405+
return new SphinxSettings(
406+
node.getStringMemberOrDefault("format", "html"),
407+
node.getStringMemberOrDefault("theme", "alabaster"),
408+
extraDependencies,
409+
node.getBooleanMemberOrDefault("autoBuild", true)
410+
);
411+
}
412+
}
291413
}

0 commit comments

Comments
 (0)