Skip to content

Commit 7936c60

Browse files
Merge pull request #10 from smithy-lang/sphinx-config
Support sphinx integration configuration
2 parents a1ee495 + 1261271 commit 7936c60

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)