forked from LinuxCNC/linuxcnc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSubmakefile
More file actions
1498 lines (1368 loc) · 67 KB
/
Copy pathSubmakefile
File metadata and controls
1498 lines (1368 loc) · 67 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
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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.PHONY: docs docclean
.PHONY: pdfdocs htmldocs install-doc install-doc-pdf install-doc-html
SHELL=/bin/bash
# Ruby (asciidoctor / asciidoctor-pdf) reads source files in the locale's
# default external encoding. Containerised builds often inherit POSIX/C,
# which makes Ruby treat every UTF-8 byte > 0x7f as an invalid sequence
# and abort on the first non-ASCII character. Force UTF-8.
export LANG := C.UTF-8
export LC_ALL := C.UTF-8
# Optional helper to convert to uppercase for variable names like $(DOC_TARGETS_HTML_SV)
# GNU make doesn’t have built-in uppercase, so define one:
uc = $(shell echo $(1) | tr '[:lower:]' '[:upper:]')
# Pure-make uppercasing (no shell). Underscores/digits are unchanged.
toUC = $(strip \
$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F, \
$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L, \
$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R, \
$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X, \
$(subst y,Y,$(subst z,Z,$(1))))))))))))))))))))))))))))
ECHO ?= /usr/bin/echo
SRCDIR=../src
DOC_DIR=../docs
DOC_SRCDIR=../docs/src
# Asciidoctor extensions loaded via -r for every doc render; a prerequisite of
# the HTML and PDF rules so editing one rebuilds the affected docs.
DOC_EXTENSIONS=$(wildcard $(DOC_SRCDIR)/extensions/*.rb)
# Unified output tree. All asciidoctor-generated artefacts (HTML, PDF,
# po4a-translated .adoc) live under DOC_BUILD so the source tree stays
# clean and the rendered docs can be tarballed or rsynced as one unit.
#
# docs/build/html/index.html # static redirect -> en/index.html
# docs/build/html/{asciidoctor,rouge-github,lcnc-overrides}.css
# docs/build/html/<lang>/index.html
# docs/build/html/<lang>/<topic>/<page>.html
# docs/build/html/<lang>/man/manN/<page>.html
# docs/build/html/<lang>/pdf/LinuxCNC_*_<lang>.pdf
# docs/build/adoc/<lang>/... # po4a-translated source tree
#
# Manpage troff is generated under build/, not committed to the source tree.
# Written by halcompile and the docs build; consumed by install-man and the
# run-in-place MANPATH.
#
# Layout follows the /usr/share/man hierarchy, NOT the build/html/<lang> and
# build/adoc/<lang> convention used everywhere else in this tree:
#
# docs/build/man/manN # English (the C / default locale)
# docs/build/man/<lang>/manN # translations, keyed by locale
#
# This asymmetry is deliberate. man(1) resolves a MANPATH entry by locale,
# reading <path>/<locale>/manN for a translation and falling back to
# <path>/manN for the default locale; putting English under an en/ subdir
# would break a plain `MANPATH=.../build/man` under LANG=C. The cost is that
# the English man tree is special-cased below (top-level manN, no en/ prefix),
# unlike html/adoc which key every language including English under <lang>/.
DOC_BUILD := $(DOC_DIR)/build
DOC_OUT_HTML := $(DOC_BUILD)/html
DOC_OUT_ADOC := $(DOC_BUILD)/adoc
DOC_MAN := $(DOC_BUILD)/man
# PDFs live under the html tree (docs/build/html/<lang>/pdf/) so the html
# subtree is self-contained and can link them with relative paths.
ASCIIDOCTOR_DEFAULT_CSS := $(shell ruby -e 'require "asciidoctor"; print Asciidoctor::DATA_DIR' 2>/dev/null)/stylesheets/asciidoctor-default.css
# The following line determines for the Makefile what languages should be addressed.
# Edit the file po4a.cfg in this source tree to adjust.
LANGUAGES := $(strip $(shell sed -e's/#.*//' < $(DOC_DIR)/po4a.cfg | grep '^\[po4a_langs\]' | cut -d" " -f2-))
LANGUAGES_MATCH := $(shell echo $(LANGUAGES) | tr " " "|")
# Native switcher labels read from docs/src/lang-labels (tag<ws>name);
# a tag with no entry falls back to its code with a warning.
LANG_LABEL_FILE := $(DOC_SRCDIR)/lang-labels
define LANG_LABEL_template
LANG_LABEL_$(1) := $(or $(shell sed -ne 's/^$(1)[[:space:]][[:space:]]*//p' $(LANG_LABEL_FILE)),$(1)$(warning lang-labels: no entry for '$(1)', using tag))
endef
$(foreach L,en $(LANGUAGES),$(eval $(call LANG_LABEL_template,$(L))))
# Minimum per-master translation completeness (percent) below which the
# language-switcher post-process pass greys out a page's entry for that
# language. The translated HTML is still emitted (po4a runs with --keep 0)
# so deep links never 404; the post-process just demotes <a> to <span>.
# Default 80% matches po4a's own default and the MDN/Hugo/Sphinx
# convention. Translators previewing work-in-progress can lower it:
# `make POKEEP=30 docs`.
POKEEP ?= 80
GENERATED_MANPAGES += $(DOC_MAN)/man1/linuxcnc.1
GENERATED_MANPAGES += $(patsubst ../docs/src/man/%.adoc, $(DOC_MAN)/%, $(wildcard ../docs/src/man/man?/*.adoc))
MAN_SRCS = $(sort \
$(wildcard $(DOC_MAN)/man1/*.1) \
$(wildcard $(DOC_MAN)/man3/*.3) \
$(wildcard $(DOC_MAN)/man9/*.9) \
$(GENERATED_MANPAGES))
# asciidoctor names the troff from the page's NAME section, not the make
# target, so a translated NAME can misname the file and collide with another
# page under -j. Pin the name with -o. NAME drift itself is reported
# separately by scripts/manpage-name-check.py (a CI warning), not here, to
# keep the build output quiet.
define MAKE_MANPAGE
@mkdir -p $(dir $@)
$(Q)asciidoctor --doctype=manpage \
--backend=manpage \
-o "$@" \
-a compat-mode \
-a mansource=LinuxCNC \
-a manmanual='LinuxCNC Documentation' \
$<
endef
$(DOC_OUT_ADOC)/en/man/man1/linuxcnc.1.adoc: $(DOC_SRCDIR)/man/man1/linuxcnc.1.adoc.in $(SRCDIR)/config.status
@mkdir -p $(@D)
$(SRCDIR)/config.status --file=$@:$<
# linuxcnc.1 troff comes from the build-tree-generated .adoc above; the
# generic pattern rule at the end of this file expects sources under
# $(DOC_DIR)/src/man/, so override with an explicit rule.
$(DOC_MAN)/man1/linuxcnc.1: $(DOC_OUT_ADOC)/en/man/man1/linuxcnc.1.adoc
$(ECHO) Making manpage $(notdir $@)
$(MAKE_MANPAGE)
info::
@echo "I: Expecting the following languages: $(LANGUAGES)"
echo $(DOC_SRCS_UK)
ifeq ($(BUILD_DOCS),yes)
DOC_SRCS_EN := \
code/code-notes.adoc \
code/style-guide.adoc \
code/nml-messages.adoc \
code/rs274.adoc \
code/adding-configs.adoc \
code/contributing-to-linuxcnc.adoc \
code/building-linuxcnc.adoc \
code/writing-tests.adoc \
common/emc-history.adoc \
common/glossary.adoc \
common/gpld-copyright.adoc \
common/overleaf.adoc \
config/core-components.adoc \
config/ini-config.adoc \
config/mtconnect.adoc \
config/ini-homing.adoc \
config/integrator-concepts.adoc \
config/lathe-config.adoc \
config/moveoff.adoc \
config/pncconf.adoc \
config/python-hal-interface.adoc \
config/python-lcnc_realtime.adoc \
config/python-interface.adoc \
config/stepconf.adoc \
config/stepper-diagnostics.adoc \
config/stepper.adoc \
config/stepper-quickstart.adoc \
drivers/ax5214h.adoc \
drivers/vfs11.adoc \
drivers/mitsub-vfd.adoc \
drivers/gm.adoc \
drivers/gs2.adoc \
drivers/hal_gpio.adoc \
drivers/hal_pi_gpio.adoc \
drivers/hostmot2.adoc \
drivers/mb2hal.adoc \
drivers/mesa_modbus.adoc \
drivers/motenc.adoc \
drivers/opto22.adoc \
drivers/pico-ppmc.adoc \
drivers/pluto-p.adoc \
drivers/pmx485.adoc \
drivers/servo-to-go.adoc \
drivers/shuttle.adoc \
examples/gcode.adoc \
examples/gs2-example.adoc \
examples/mpg.adoc \
examples/pci-parallel-port.adoc \
examples/spindle.adoc \
gcode/coordinates.adoc \
gcode/g-code.adoc \
gcode/machining-center.adoc \
gcode/m-code.adoc \
gcode/o-code.adoc \
gcode/other-code.adoc \
gcode/overview.adoc \
gcode/rs274ngc.adoc \
gcode/tool-compensation.adoc \
getting-started/about-linuxcnc.adoc \
getting-started/getting-linuxcnc.adoc \
common/linux-faq.adoc \
getting-started/running-linuxcnc.adoc \
getting-started/system-requirements.adoc \
getting-started/hardware-interface.adoc \
getting-started/updating-linuxcnc.adoc \
gui/axis.adoc \
gui/filter-programs.adoc \
gui/gladevcp.adoc \
gui/gladevcp-panels.adoc \
gui/gladevcp-libraries.adoc \
gui/gmoccapy.adoc \
gui/gscreen.adoc \
gui/qtdragon.adoc \
gui/qtvcp.adoc \
gui/qtvcp-vcp-panels.adoc \
gui/qtvcp-widgets.adoc \
gui/qtvcp-libraries.adoc \
gui/qtvcp-vismach.adoc \
gui/qtvcp-custom-widgets.adoc \
gui/qtvcp-code-snippets.adoc \
gui/qtvcp-development.adoc \
gui/panelui.adoc \
gui/halui.adoc \
gui/image-to-gcode.adoc \
gui/mdro.adoc \
gui/ngcgui.adoc \
gui/tklinuxcnc.adoc \
gui/tooledit.adoc \
gui/touchy.adoc \
gui/gstat.adoc \
gui/vismach.adoc \
gui/gui-dev-reference.adoc \
hal/basic-hal.adoc \
hal/canonical-devices.adoc \
hal/comp.adoc \
hal/components.adoc \
hal/general-ref.adoc \
hal/hal-examples.adoc \
hal/halmodule.adoc \
hal/haltcl.adoc \
hal/halui-examples.adoc \
hal/intro.adoc \
hal/parallel-port.adoc \
gui/pyvcp.adoc \
gui/pyvcp-examples.adoc \
hal/rtcomps.adoc \
hal/tools.adoc \
hal/tutorial.adoc \
hal/twopass.adoc \
install/latency-test.adoc \
integrator/steppers.adoc \
integrator/stepper-timing.adoc \
integrator/wiring.adoc \
ladder/classic-ladder.adoc \
ladder/ladder-examples.adoc \
ladder/ladder-intro.adoc \
lathe/lathe-user.adoc \
motion/kinematics.adoc \
motion/kinematics-conventions.adoc \
motion/dh-parameters.adoc \
motion/pid-theory.adoc \
motion/dual-pid-example.adoc \
motion/tweaking-steppers.adoc \
motion/5-axis-kinematics.adoc \
motion/external-offsets.adoc \
motion/switchkins.adoc \
tooldatabase/tooldatabase.adoc \
plasma/qtplasmac.adoc \
plasma/plasma-cnc-primer.adoc \
remap/remap.adoc \
user/starting-linuxcnc.adoc \
user/user-concepts.adoc \
user/user-foreword.adoc \
user/user-intro.adoc \
Master_Getting_Started.adoc \
Master_Documentation.adoc \
Master_Integrator.adoc \
Master_Developer.adoc
# Map two-letter codes to human-readable language names
# (this can extended on demand to handle zh_TW, etc.)
define lang_name
$(if $(filter $1,ar),arabic,\
$(if $(filter $1,de),german,\
$(if $(filter $1,en),english,\
$(if $(filter $1,es),spanish,\
$(if $(filter $1,fr),french,\
$(if $(filter $1,nb),norwegian,\
$(if $(filter $1,ru),russian,\
$(if $(filter $1,sv),swedish,\
$(if $(filter $1,ta),tamil,\
$(if $(filter $1,tr),turkish,\
$(if $(filter $1,uk),ukranian,\
$(if $(filter $1,zh_CN),chinese,\
unknown))))))))))))
endef
GENERATED_TRANSLATED = $(foreach l, $(LANGUAGES), \
$(DOC_OUT_ADOC)/$(l) \
$(DOC_OUT_HTML)/$(l) \
$(DOC_MAN)/$(l) \
)
# Time how long po4a takes to run if the system has the `time` command
# installed, but just skip timing and don't fail the build if it's
# not available.
TIME_CMD := $(shell which time || true)
ifneq (${TIME_CMD},)
TIME_CMD := ${TIME_CMD} -v
endif
ifeq ($(BUILD_VERBOSE),1)
PO4A_VERBOSE = -v
else
PO4A_VERBOSE =
endif
# components_gen.adoc is referenced as a translation master in po4a.cfg
# (so each language has its own translated copy in <lang>/hal/) and is
# auto-generated from the English manpages by gen_complist.py. It must
# exist before po4a is invoked, otherwise po4a aborts with "master file
# does not exist". gen_complist.py reads the manpage source list from
# $(DOC_MAN)/, so changes to that set must invalidate the file --
# hence $(MAN_SRCS) as a real prereq (the script is content-stable via
# write_if_changed, so re-running over the same set is a no-op for
# mtime, which keeps downstream po4a from re-firing every build).
$(DOC_OUT_ADOC)/en/hal/components_gen.adoc: $(DOC_SRCDIR)/gen_complist.py $(DOC_SRCDIR)/hal/components.adoc $(MAN_SRCS) | manpages
@mkdir -p $(dir $@)
python3 $(DOC_SRCDIR)/gen_complist.py $(DOC_SRCDIR)/hal/components.adoc
$(DOC_DIR)/po/documentation.pot: $(addprefix $(DOC_SRCDIR)/, $(DOC_SRCS_EN)) $(DOC_OUT_ADOC)/en/hal/components_gen.adoc
cd $(DOC_DIR) && ${TIME_CMD} po4a $(PO4A_VERBOSE) --msgmerge-opt='-v' --no-translations po4a.cfg
@touch $@
pofiles: $(DOC_DIR)/po/documentation.pot
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
# Stamp-file gate: without it `translateddocs` is a phony-style target with
# no file output, so make re-runs po4a every build. po4a rewrites the
# per-language .adoc files, bumping their mtime past the downstream PDFs
# and triggering a full translated-PDF rebuild on every invocation.
#
# Depend on the committed .po files and components_gen.adoc (a po4a master),
# NOT documentation.pot: regenerating the pot runs po4a in msgmerge mode,
# rewriting every docs/po/*.po with a fresh POT-Creation-Date on each build
# (dirty tree + mtime cascade). Building needs only `po4a --no-update`;
# pot/po extraction stays on the explicit `pofiles` target.
$(DOC_DIR)/.translateddocs-stamp: $(DOC_OUT_ADOC)/en/hal/components_gen.adoc $(wildcard $(DOC_DIR)/po/*.po) | manpages
cd $(DOC_DIR) && ${TIME_CMD} po4a $(PO4A_VERBOSE) --msgmerge-opt='-v' --no-update po4a.cfg
@touch $@
translateddocs: $(DOC_DIR)/.translateddocs-stamp
else
translateddocs:
endif
docs: translateddocs
postatus::
@$(ECHO) "info: Documentation POT and PO file status"
@cd $(DOC_DIR)/..; for p in docs/po/*.pot docs/po/*.po; do \
echo -n "$$p "; msgfmt --statistics -o /dev/null $$p; \
done
# Automatically define DOC_SRCS_<lang> for each language by reading the
# translated-file list straight out of po4a.cfg. Deriving from po4a.cfg
# (rather than scanning $(L)/ with $(wildcard) or assuming every English
# source has a translation) gives two properties at once: the list does
# not depend on po4a having already produced the files (no "build twice"
# fragility), and it does not include English-only sources like
# drivers/mesa_modbus.adoc that are absent from the translation pipeline.
TRANSLATED_DOC_SRCS := $(shell sed -nE 's|^\[type: AsciiDoc_def\] src/([^ ]+).*|\1|p' \
$(DOC_DIR)/po4a.cfg)
$(foreach L,$(LANGUAGES),\
$(eval DOC_SRCS_$(call toUC,$(L)) := \
$$(addprefix $(L)/,$$(TRANSLATED_DOC_SRCS))))
# Define combined DOC_SRCS from all languages. Translated sources are
# only pulled in when BUILD_DOCS_TRANSLATED=yes; otherwise the HTML / PDF
# target lists derived from DOC_SRCS stay English-only and po4a is never
# invoked.
# DOC_SRCS_EN is manually defined at the very beginning of the file
DOC_SRCS := $(DOC_SRCS_EN)
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
DOC_SRCS += $(foreach L,$(LANGUAGES),$(DOC_SRCS_$(call toUC,$L)))
endif
$(foreach L,en $(LANGUAGES),\
$(eval DOC_SRCS_$(call toUC,$(L))_SMALL := \
$(filter-out Master_% man/% $(L)/man/%,$(DOC_SRCS_$(call toUC,$(L))))))
# Manpages are produced by the translated-manpage pipeline further
# down (asciidoctor --doctype=manpage), not the generic adoc-to-html
# rule. Filter them out so DOC_TARGETS_HTML_<L> does not claim the
# same html/<L>/man/... output paths.
DOC_SRCS_HTML = $(patsubst %.adoc, %.html, $(foreach p, $(DOC_SRCS), \
$(if $(findstring Master_, $(p)),, $(if $(findstring /man/, /$(p)),, $(p)))))
# Per-language HTML target lists. English files have no lang/ prefix in
# their source path; translated files already include $(L)/ in DOC_SRCS_$L.
# The English bucket gets an explicit en/ prefix so every language lives
# under its own subtree under $(DOC_OUT_HTML).
DOC_TARGETS_HTML_EN := $(addprefix $(DOC_OUT_HTML)/en/, \
$(filter-out $(foreach L,$(LANGUAGES),$(L)/%), \
$(patsubst %.adoc,%.html, \
$(filter-out Master_%, $(DOC_SRCS_EN)))))
# Page stems English builds (Master_* are PDF-only; include::d partials are not
# pages). Translations are filtered to this set: the per-language list comes
# from po4a.cfg, which lists partials too, so without it a partial renders
# standalone with a dead en/ language-switcher link.
DOC_HTML_PAGE_STEMS := $(patsubst %.adoc,%.html,$(filter-out Master_% man/%,$(DOC_SRCS_EN)))
$(foreach L,$(LANGUAGES), \
$(eval DOC_TARGETS_HTML_$(call toUC,$(L)) := \
$$(addprefix $(DOC_OUT_HTML)/$(L)/, \
$$(filter $$(DOC_HTML_PAGE_STEMS), \
$$(patsubst $(L)/%.adoc,%.html, \
$$(filter-out $(L)/Master_% $(L)/man/%, \
$$(DOC_SRCS_$(call toUC,$(L)))))))))
DOC_TARGETS_HTML = $(DOC_TARGETS_HTML_EN)
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
DOC_TARGETS_HTML += $(foreach L,$(LANGUAGES),$(DOC_TARGETS_HTML_$(call toUC,$(L))))
endif
DOC_TARGETS_XML_EN = $(patsubst $(DOC_OUT_HTML)/en/%.html, objects/%.xml, $(DOC_TARGETS_HTML_EN))
$(foreach L,$(LANGUAGES), \
$(eval DOC_TARGETS_XML_$(call toUC,$(L)) = \
$$(patsubst $(DOC_OUT_HTML)/%.html, objects/%.xml, $$(DOC_TARGETS_HTML_$(call toUC,$(L))))))
DOC_TARGETS_XML = $(DOC_TARGETS_XML_EN)
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
DOC_TARGETS_XML += $(foreach L,$(LANGUAGES),$(DOC_TARGETS_XML_$(call toUC,$(L))))
endif
# Manpage HTML. English manpages live at docs/build/html/en/man/manN/X.html;
# translated manpages mirror the layout under their own lang dir. The
# per-lang list is derived from po4a.cfg (authoritative source of which
# manpages have a translated counterpart) rather than MAN_SRCS, which
# would close a cycle through components_gen.adoc and over-include.
# Every language renders the FULL English manpage set so the docs stay
# uniform: translated where a po4a leg provides it, English fallback
# otherwise (the per-page banner shows the completeness).
MAN_EN_STEMS := $(patsubst $(DOC_MAN)/%, %, $(MAN_SRCS))
MAN_HTML_TARGETS_EN := $(addprefix $(DOC_OUT_HTML)/en/man/, $(addsuffix .html, $(MAN_EN_STEMS)))
# Stems that get a po4a-built per-language troff: AsciiDoc_def (committed
# src/man/*.adoc, rendered per-lang via TRANSLATED_TROFF_RULE) and man_def
# (the comp manpages, translated troff direct). Everything else is
# English-only and gets an English troff copy per language (rule below) so
# the per-language target set can be the full English set.
PO4A_MANPAGE_STEMS := $(shell sed -ne 's|^\[type: AsciiDoc_def\] src/man/\([^ ]*\)\.adoc .*|\1|p' $(DOC_DIR)/po4a.cfg)
PO4A_MANPAGE_STEMS += $(shell sed -ne 's|^\[type: man_def\] build/man/\([^ ]*\) .*|\1|p' $(DOC_DIR)/po4a.cfg)
MAN_ENONLY_STEMS := $(filter-out $(sort $(PO4A_MANPAGE_STEMS)), $(MAN_EN_STEMS))
$(foreach L,$(LANGUAGES), \
$(eval MAN_HTML_TARGETS_$(call toUC,$(L)) := \
$$(addprefix $(DOC_OUT_HTML)/$(L)/man/, \
$$(addsuffix .html, $$(MAN_EN_STEMS)))))
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
MAN_HTML_TARGETS = $(MAN_HTML_TARGETS_EN) \
$(foreach L,$(LANGUAGES),$(MAN_HTML_TARGETS_$(call toUC,$(L))))
else
MAN_HTML_TARGETS = $(MAN_HTML_TARGETS_EN)
endif
# PDFs live in their own subtree alongside html/ and adoc/ so the html
# tree zips up cleanly without PDF bloat, matching the rest of the
# docs/build/ layout convention.
PDF_TARGETS_EN := $(addprefix $(DOC_OUT_HTML)/en/pdf/, \
$(patsubst %.adoc,%_en.pdf, \
$(subst Master_,LinuxCNC_, $(filter Master_%,$(DOC_SRCS_EN)))) \
LinuxCNC_Manual_Pages_en.pdf)
$(foreach L,$(LANGUAGES), \
$(eval PDF_TARGETS_$(call toUC,$(L)) = \
$$(addprefix $(DOC_OUT_HTML)/$(L)/pdf/, \
$$(subst $(L)/,, \
$$(patsubst %.adoc,%_$(L).pdf, \
$$(subst Master_,LinuxCNC_, $$(filter $(L)/Master_%,$$(DOC_SRCS_$(call toUC,$(L))))))))))
PDF_TARGETS = $(PDF_TARGETS_EN)
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
PDF_TARGETS += $(foreach L,$(LANGUAGES),$(PDF_TARGETS_$(call toUC,$(L))))
endif
info::
@$(ECHO) PDF_TARGETS: $(PDF_TARGETS)
# It's better to keep the above on separate lines for troubleshooting by swapping
HTML_TARGETS = \
$(DOC_TARGETS_HTML) \
$(MAN_HTML_TARGETS) \
$(DOC_OUT_HTML)/index.html \
$(DOC_OUT_HTML)/en/index.html \
$(sort $(patsubst $(DOC_OUT_ADOC)/%/index.tmpl, \
$(DOC_OUT_HTML)/%/index.html, \
$(wildcard $(DOC_OUT_ADOC)/*/index.tmpl)))
ifeq ($(TRIVIAL_BUILD),no)
-include $(patsubst %.adoc, depends/%.d, $(DOC_SRCS))
Makefile: $(patsubst %.adoc, depends/%.d, $(DOC_SRCS))
endif
docs: manpages
clean: clean-manpages clean-translated
-rm -f $(DOC_DIR)/html
clean-manpages:
-rm -f $(GENERATED_MANPAGES)
# Remove generated alias man pages too.
$(RM) $$(grep -lr '^\.so ' $(DOC_MAN)/man*)
clean-translated:
-$(RM) -r $(GENERATED_TRANSLATED)
# Docs .dot render into build/adoc/en/<topic> so docs/src stays clean. Other
# .dot (e.g. src/emc/motion/homing.dot, not a doc) still render beside source.
DOC_DOTFILES := $(shell find $(DOC_SRCDIR) -name '*.dot')
DOC_DOT_SVGS := $(patsubst $(DOC_SRCDIR)/%.dot,$(DOC_OUT_ADOC)/en/%.svg,$(DOC_DOTFILES))
OTHER_DOTFILES := $(filter-out $(DOC_DOTFILES),$(shell find . -name '*.dot'))
.PHONY: svgs_made_from_dots
svgs_made_from_dots: $(DOC_DOT_SVGS) $(OTHER_DOTFILES:.dot=.svg)
$(DOC_OUT_ADOC)/en/%.svg: $(DOC_SRCDIR)/%.dot
@mkdir -p $(@D)
dot -Tsvg -Gbgcolor=transparent -o$@ $<
# Pattern-rule outputs; .SECONDARY so the -j build keeps them (see staging rule).
.SECONDARY: $(DOC_DOT_SVGS)
ifeq ($(BUILD_DOCS_PDF),yes)
docs: pdfdocs
install-doc: install-doc-pdf
endif
ifeq ($(BUILD_DOCS_HTML),yes)
docs: htmldocs
install-doc: install-doc-html
docs: $(DOC_DIR)/html
endif
# Legacy publish path: the website sync uploads docs/html/, which the unified
# build moved to docs/build/html/. Expose docs/html as a symlink to the new
# tree so the (dereferencing) sync keeps publishing. git-ignored, clean'd.
$(DOC_DIR)/html:
$(Q)ln -sfn build/html $@
# debian/linuxcnc-doc-en.docs installs docs/build/html/en/gcode.html, so the
# English gcode reference must land in the build tree even when only PDF docs
# are enabled (debian/configure passes --enable-build-documentation=pdf, so
# .copy-asciidoc-stamp -- which lives under htmldocs -- does not fire).
# Per-lang topbar fragments. Pre-substituted versions of docinfo-header.html
# with lcnc-cssrel and lcnc-lang-label resolved; consumed by the gcode.html
# and index.html rules below, which still need to substitute {lcnc-subpath}.
objects/topbar-en.html: $(DOC_SRCDIR)/docinfo-header.html
@mkdir -p $(@D)
@sed -e 's|{lcnc-cssrel}|../|g' -e 's|{lcnc-lang-label}|$(LANG_LABEL_en)|g' $< > $@
$(foreach L,$(LANGUAGES),objects/topbar-$(L).html): objects/topbar-%.html: $(DOC_SRCDIR)/docinfo-header.html
@mkdir -p $(@D)
@sed -e 's|{lcnc-cssrel}|../|g' -e 's|{lcnc-lang-label}|$(LANG_LABEL_$*)|g' $< > $@
# debian/linuxcnc-doc-en.docs installs docs/build/html/en/gcode.html, so the
# English gcode reference must land in the build tree even when only PDF docs
# are enabled (debian/configure passes --enable-build-documentation=pdf, so
# .copy-asciidoc-stamp -- which lives under htmldocs -- does not fire).
# Wraps the .in template with the English topbar fragment and rewrites the
# CSS path (the source sits at docs/build/html/en/, css at docs/build/html/).
docs: $(DOC_OUT_HTML)/en/gcode.html
$(DOC_OUT_HTML)/en/gcode.html: $(DOC_SRCDIR)/gcode.html.in objects/topbar-en.html $(DOC_SRCDIR)/Submakefile
@mkdir -p $(@D)
$(Q){ \
topbar=$$(mktemp) ; \
sed 's|{lcnc-subpath}|gcode.html|g' objects/topbar-en.html > $$topbar ; \
awk -v t="$$topbar" 'BEGIN{ while ((getline line < t) > 0) buf = buf line "\n" } /<body>/{ print; printf "%s", buf; next } { print }' $< \
| sed 's|href="lcnc-overrides.css"|href="../lcnc-overrides.css"|g' > $@ ; \
rm -f $$topbar ; \
}
# Per-language gcode.html: po4a translates docs/src/gcode.html.in into
# $(DOC_OUT_ADOC)/<lang>/gcode-raw.html. Wrap each raw output with the
# lang-specific topbar to produce the final $(DOC_OUT_HTML)/<lang>/gcode.html.
$(foreach L,$(LANGUAGES),$(DOC_OUT_HTML)/$(L)/gcode.html): $(DOC_OUT_HTML)/%/gcode.html: objects/topbar-%.html $(DOC_SRCDIR)/Submakefile | translateddocs
@mkdir -p $(dir $@)
$(Q){ \
topbar=$$(mktemp) ; \
sed 's|{lcnc-subpath}|gcode.html|g' objects/topbar-$*.html > $$topbar ; \
awk -v t="$$topbar" 'BEGIN{ while ((getline line < t) > 0) buf = buf line "\n" } /<body>/{ print; printf "%s", buf; next } { print }' $(DOC_OUT_ADOC)/$*/gcode-raw.html \
| sed 's|href="lcnc-overrides.css"|href="../lcnc-overrides.css"|g' > $@ ; \
rm -f $$topbar ; \
}
# Hook per-lang gcode.html into the docs target when translations are on.
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
docs: $(foreach L,$(LANGUAGES),$(DOC_OUT_HTML)/$(L)/gcode.html)
endif
pdfdocs: svgs_made_from_dots $(PDF_TARGETS) $(DOC_OUT_HTML)/pdf/index.html
# Flat aggregate of every language's PDF (hardlinks) plus a listing, so the
# legacy flat /pdf/ URL keeps working next to the per-language html/<lang>/pdf/
# trees. Real file target so a no-op second build does not re-fire the script.
$(DOC_OUT_HTML)/pdf/index.html: $(PDF_TARGETS) ../scripts/make-docs-pdf-index
@mkdir -p $(dir $@)
$(Q)../scripts/make-docs-pdf-index
htmldocs: svgs_made_from_dots .htmldoc-stamp
# When translations are enabled, the .adoc files in $(L)/ are produced by
# the translateddocs target (po4a). Teach make how to ask for them: the
# rule below has the translated .adoc files depend on translateddocs as
# an order-only prereq, with an empty recipe. That gives make a "rule"
# to satisfy them (so it does not stop with "No rule to make target ..."
# when depends/%.d is evaluated on a fresh tree) without rebuilding them
# every time translateddocs ticks.
ifeq ($(BUILD_DOCS_TRANSLATED),yes)
TRANSLATED_ADOC_TARGETS := $(addprefix $(DOC_OUT_ADOC)/, \
$(filter-out $(DOC_SRCS_EN), $(DOC_SRCS)))
$(TRANSLATED_ADOC_TARGETS): | translateddocs ;
endif
# Depend on the stamp files, not the phony copy_asciidoc_files /
# gen_complist aliases: a phony prereq is always "newer", so naming them
# re-touched .htmldoc-stamp every run, dragging the css copy
# with it. The stamps fire only when their real inputs change.
.htmldoc-stamp: .copy-asciidoc-stamp $(DOC_DIR)/.gen_complist-stamp $(HTML_TARGETS) .images-stamp .include-stamp $(DOC_OUT_HTML)/asciidoctor.css $(DOC_OUT_HTML)/rouge-github.css .lang-switcher-stamp
touch $@
# Inject the whole-document sidebar/topbar and grey out missing language-
# switcher entries. Runs last (depends on every HTML target) and is
# idempotent. Gated on BUILD_DOCS_HTML, not translations: the sidebar comes
# from the Master_*.adoc structure, so English-only builds need it too.
ifeq ($(BUILD_DOCS_HTML),yes)
.lang-switcher-stamp: $(DOC_SRCDIR)/lang_switcher_postprocess.py $(HTML_TARGETS) $(TRANSLATED_MAN_HTML_TARGETS) $(wildcard $(DOC_DIR)/po/*.po)
$(Q)python3 $(DOC_SRCDIR)/lang_switcher_postprocess.py $(DOC_OUT_HTML) $(DOC_DIR)/po $(POKEEP) $(LANGUAGES)
@touch $@
else
.lang-switcher-stamp:
@touch $@
endif
# Shared assets at the top of the html tree. Index templates and the
# asciidoctor docinfo reference them via ../ relative paths so we ship
# one copy regardless of how many languages render. gcode.html is NOT
# shared: po4a translates it per-language and the per-lang index links
# to its sibling gcode.html.
SHARED_HTML_ASSETS = \
$(DOC_SRCDIR)/lcnc-overrides.css \
$(DOC_SRCDIR)/lcnc-docs.svg \
$(DOC_SRCDIR)/index.css \
$(DOC_SRCDIR)/linuxcnc-logo-chips.png \
$(DOC_SRCDIR)/admon-note.svg \
$(DOC_SRCDIR)/admon-tip.svg \
$(DOC_SRCDIR)/admon-important.svg \
$(DOC_SRCDIR)/admon-warning.svg \
$(DOC_SRCDIR)/admon-caution.svg
# docinfo-header.html: generated from .in template. The @LANGUAGE_SWITCHER@
# placeholder expands to one <li> per language (English plus everything
# in $(LANGUAGES)), labelled via $(LANG_LABEL_<lang>). asciidoctor then
# substitutes the {lcnc-cssrel} / {lcnc-lang-label} / {lcnc-subpath}
# attributes per page. po4a.cfg drives the language list, lang-labels
# the display names.
$(DOC_SRCDIR)/docinfo-header.html: $(DOC_SRCDIR)/docinfo-header.html.in $(DOC_DIR)/po4a.cfg $(LANG_LABEL_FILE) $(DOC_SRCDIR)/Submakefile
@block=$$(mktemp); \
printf ' <div class="lcnc-lang-switcher">\n' > $$block ; \
printf ' <input type="checkbox" id="lcnc-lang-toggle" class="lcnc-lang-toggle">\n' >> $$block ; \
printf ' <label for="lcnc-lang-toggle" class="lcnc-lang-label">{lcnc-lang-label}</label>\n' >> $$block ; \
printf ' <ul class="lcnc-lang-list">\n' >> $$block ; \
printf ' <li><a href="{lcnc-cssrel}en/{lcnc-subpath}">$(LANG_LABEL_en)</a></li>\n' >> $$block ; \
$(foreach L,$(LANGUAGES),printf ' <li><a href="{lcnc-cssrel}$(L)/{lcnc-subpath}">$(LANG_LABEL_$(L))</a></li>\n' >> $$block ;) \
printf ' </ul>\n' >> $$block ; \
printf ' </div>\n' >> $$block ; \
awk -v block="$$block" ' \
/@LANGUAGE_SWITCHER@/ { while ((getline line < block) > 0) print line; next } \
{ print } \
' $< > $@ ; \
rm -f $$block
# Stamp-gated asset copy; copy_asciidoc_files stays as a phony alias.
# The en/gcode.html copy lives in its own rule above so it fires for PDF-only
# builds as well; .copy-asciidoc-stamp needs it built for HTML builds but only
# copies the shared assets, so gcode.html is an order-only prerequisite: the
# post-processor later rewrites gcode.html in place, and a normal prerequisite
# would then leave this stamp stale and re-fire the copy every make.
copy_asciidoc_files: .copy-asciidoc-stamp
.copy-asciidoc-stamp: $(SHARED_HTML_ASSETS) | $(DOC_OUT_HTML)/en/gcode.html
@mkdir -p $(DOC_OUT_HTML)
cp -f $(SHARED_HTML_ASSETS) $(DOC_OUT_HTML)
@touch $@
$(DOC_OUT_HTML)/asciidoctor.css: $(ASCIIDOCTOR_DEFAULT_CSS)
@mkdir -p $(dir $@)
cp -f $< $@
# Rouge ships theme CSS in the gem; with -a linkcss asciidoctor emits
# <link href="<stylesdir>/rouge-<style>.css"> per page but does not copy
# the file when -a copycss! is set. Render the theme via the Rouge API
# so the spans asciidoctor emits get coloured. Combine light + dark
# variants (dark gated on prefers-color-scheme) into one file.
$(DOC_OUT_HTML)/rouge-github.css: $(DOC_SRCDIR)/render-rouge-css.rb
@mkdir -p $(dir $@)
ruby $< > $@
# Stamp-file gate. The phony alias was running the python script on
# every build, rewriting components_gen.adoc with a fresh mtime, which
# in turn invalidated documentation.pot and re-triggered po4a/HTML.
#
# components_gen.adoc itself is produced by the lighter rule above
# (line ~252) which fires before po4a; that rule's $(MAN_SRCS) prereq
# keeps the file in sync with the manpage source set. Re-running
# gen_complist.py here after MAN_HTML_TARGETS would rewrite the file
# with HTML-existence-dependent content (different miss_in_man set),
# bumping mtime past .pot and re-triggering po4a on the next build.
# MAN_HTML_TARGETS is order-only: this stamp only needs the manpages built,
# it does not read their content, and the post-processor rewrites them in
# place later -- a normal prerequisite would then re-fire this stamp (and the
# .htmldoc-stamp that depend on it) on every subsequent make.
$(DOC_DIR)/.gen_complist-stamp: $(DOC_OUT_ADOC)/en/hal/components_gen.adoc | $(MAN_HTML_TARGETS)
mkdir -p $(DOC_OUT_HTML)/en/hal
@touch $@
gen_complist: $(DOC_DIR)/.gen_complist-stamp
MAN_SRCS_NOSO = $(patsubst $(DOC_MAN)/%,%, \
$(shell grep -s -L '^\.so ' $(MAN_SRCS)))
PDF_MAN_ORDER := man1/linuxcnc.1 $(filter-out %/linuxcnc.1, $(filter man1/%, $(MAN_SRCS_NOSO))) \
man3/hal.3 $(filter-out %/hal.3, $(filter man3/hal%.3, $(MAN_SRCS_NOSO))) \
man3/rtapi.3 $(filter-out %/rtapi.3, $(filter man3/rtapi%.3, $(MAN_SRCS_NOSO))) \
$(filter man3/hm2%.3, $(MAN_SRCS_NOSO)) \
$(filter man9/%, $(MAN_SRCS_NOSO))
# The manual-pages PDF rule lives next to the other PDF rules
# (further down, after the CJK font setup) so it can pick up
# CJK_TTFS / DOC_FONT_DIR without forward references.
# Files produced by pattern rules but not named as explicit goals are
# treated as intermediate and deleted at the end of every build, which
# triggers a full rebuild on the next invocation. Two groups bite us:
# - per-language Master_*.pdf (chain: .adoc -> %/Master_*.pdf
# -> LinuxCNC_*_<lang>.pdf)
# - the per-manpage HTML files (chain: man/% -> html/man/%.html, used
# via $(HTML_TARGETS) but never as an explicit goal name)
# Declaring them .SECONDARY keeps them on disk without affecting precious
# -on-error semantics.
.SECONDARY: $(foreach L,$(LANGUAGES),$(foreach D,Getting_Started Documentation Integrator Developer,$(DOC_OUT_ADOC)/$(L)/Master_$(D).pdf)) \
$(foreach D,Getting_Started Documentation Integrator Developer,objects/Master_$(D).pdf) \
$(MAN_HTML_TARGETS)
# Final PDFs are hardlinks into the html tree (cheap, idempotent) from the
# asciidoctor-pdf intermediates: objects/Master_*.pdf for English,
# $(DOC_OUT_ADOC)/<lang>/Master_*.pdf for translations. Every PDF carries a
# _<lang> suffix (English too) so downloaded files keep their language.
define PDF_LINK_RULES
$(DOC_OUT_HTML)/en/pdf/LinuxCNC_$(1)_en.pdf: objects/Master_$(1).pdf
@mkdir -p $$(dir $$@)
@ln -f $$< $$@
endef
$(foreach D,Getting_Started Documentation Integrator Developer, \
$(eval $(call PDF_LINK_RULES,$(D))))
define PDF_LINK_RULES_LANG
$(DOC_OUT_HTML)/$(1)/pdf/LinuxCNC_$(2)_$(1).pdf: $(DOC_OUT_ADOC)/$(1)/Master_$(2).pdf
@mkdir -p $$(dir $$@)
@ln -f $$< $$@
endef
$(foreach L,$(LANGUAGES), \
$(foreach D,Getting_Started Documentation Integrator Developer, \
$(eval $(call PDF_LINK_RULES_LANG,$(L),$(D)))))
# Manpage HTML rule, parameterised over language tag (en / de / ...). The
# English bucket reads troff from docs/build/man/manN; translated buckets read
# from docs/build/man/<lang>/manN. cssrel is ../../../ for both: each output
# sits at docs/build/html/<lang>/man/manN/X.html (4 levels under html/).
define MAN_HTML_RULE
$(DOC_OUT_HTML)/$(1)/man/%.html: $(2)/% $(DOC_SRCDIR)/docinfo.html $(DOC_SRCDIR)/docinfo-header.html | manpages
@$$(ECHO) Formatting $$(notdir $$<) as HTML
@mkdir -p $$(dir $$@)
$$(Q)if grep -q '^\.so' $$<; then \
ln -srf $$(DOC_OUT_HTML)/$(1)/man/$$$$(basename $$$$(dirname $$<))/$$$$(basename $$$$(awk '{print $$$$2}' $$<)).html $$@; \
else \
N="$$$$(basename "$$<").adoc"; \
echo "Formatting $$$$N as HTML"; \
D="$$$$(dirname "$$<")"; \
S="$$$$(basename "$$$$D")"; \
if [ -r "$(3)/man/$$$$S/$$$$N" ]; then \
F="$(3)/man/$$$$S/$$$$N"; \
elif [ -r "$(DOC_OUT_ADOC)/$(1)/man/$$$$S/$$$$N" ]; then \
F="$(DOC_OUT_ADOC)/$(1)/man/$$$$S/$$$$N"; \
elif [ -r "objects/man/$$$$S/$$$$N" ]; then \
F="objects/man/$$$$S/$$$$N"; \
elif [ -r "$(DOC_SRCDIR)/man/$$$$S/$$$$N" ]; then \
F="$(DOC_SRCDIR)/man/$$$$S/$$$$N"; \
elif [ -r "$(DOC_OUT_ADOC)/en/man/$$$$S/$$$$N" ]; then \
F="$(DOC_OUT_ADOC)/en/man/$$$$S/$$$$N"; \
else \
echo "Error: Cannot find manpage '$$<' in adoc format"; \
exit 1; \
fi; \
asciidoctor \
-r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_hal.rb \
-r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ngc.rb \
-r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ini.rb \
-r $$(realpath $$(DOC_SRCDIR))/extensions/man_xref.rb \
--doctype=manpage \
--backend=html5 \
-a compat-mode \
-a mansource=LinuxCNC \
-a manmanual='LinuxCNC Documentation' \
-a "lang=$(1)" \
-a "lcnc-cssrel=../../../" \
-a "manxref-root=$$(realpath $(2))" \
-a "lcnc-lang-label=$(LANG_LABEL_$(1))" \
-a "lcnc-subpath=$$(patsubst $(DOC_OUT_HTML)/$(1)/%,%,$$@)" \
-a docinfo=shared \
-a docinfodir=$$(realpath $$(DOC_SRCDIR)) \
-a source-highlighter=rouge \
-a rouge-style=github \
-a webfonts! \
-a linkcss -a copycss! \
-a "stylesdir=../../../" \
-o $$@ \
"$$$$F" \
; \
fi;
endef
$(eval $(call MAN_HTML_RULE,en,$(DOC_MAN),$(DOC_SRCDIR)))
$(foreach L,$(LANGUAGES), \
$(eval $(call MAN_HTML_RULE,$(L),$(DOC_MAN)/$(L),$(DOC_OUT_ADOC)/$(L))))
#
# This function appends a section to the "manpages" HTML fragment called
# index.incl, listing all the specified manpages. It takes 3 arguments:
# * the part of the manpage space, for example "1" or "rtapi"
# * the manpage section title
# * a list of files to add to that section
#
ADD_HTML_MANPAGES = \
echo "Adding manpages: $(strip $(1)): $(strip $(2))"; \
echo "<details>" >> objects/index.incl; \
echo "<summary>man($(strip $(1))) - $(strip $(2))</summary>" >> objects/index.incl; \
echo "<div class=\"details-list\"><ul>" >> objects/index.incl; \
for HTML_FILE in $(sort $(3)); do \
BASENAME=$$(basename $$HTML_FILE .html); \
echo "<li><a href=\"$${HTML_FILE\#$(DOC_OUT_HTML)/en/}\">$${BASENAME%.*}</a></li>"; \
done >> objects/index.incl; \
echo "</ul></div></details>" >> objects/index.incl; \
# Depends on the English target list only (the index is built from it); the
# full 9-language MAN_HTML_TARGETS would overflow a single bash arg
# (MAX_ARG_STRLEN) once every language renders the whole manpage set.
objects/index.incl: $(GENERATED_MANPAGES) objects/var-MAN_HTML_TARGETS_EN $(DOC_SRCDIR)/Submakefile
rm -f $@
$(call ADD_HTML_MANPAGES, 1, Commands and userspace components, $(filter $(DOC_OUT_HTML)/en/man/man1/%.html, $(MAN_HTML_TARGETS_EN))) \
$(call ADD_HTML_MANPAGES, 9, Realtime components and kernel modules, $(filter $(DOC_OUT_HTML)/en/man/man9/%.html, $(MAN_HTML_TARGETS_EN))) \
$(call ADD_HTML_MANPAGES, 3, API: HAL, $(filter $(DOC_OUT_HTML)/en/man/man3/hal%.html, $(MAN_HTML_TARGETS_EN))) \
$(call ADD_HTML_MANPAGES, 3, API: RTAPI, $(filter $(DOC_OUT_HTML)/en/man/man3/rtapi%.html, $(MAN_HTML_TARGETS_EN))) \
$(call ADD_HTML_MANPAGES, 3, API: Hostmot2, $(filter $(DOC_OUT_HTML)/en/man/man3/hm2%.html, $(MAN_HTML_TARGETS_EN))) \
$(call ADD_HTML_MANPAGES, 3, API: General, $(filter-out $(DOC_OUT_HTML)/en/man/man3/hal%.html, $(filter-out $(DOC_OUT_HTML)/en/man/man3/rtapi%.html, $(filter-out $(DOC_OUT_HTML)/en/man/man3/hm2%.html, $(filter $(DOC_OUT_HTML)/en/man/man3/%.html, $(MAN_HTML_TARGETS_EN)))))) \
# now make sure all English manpages made it into the html index
FAIL=0; \
for F in $$(find $(DOC_MAN)/man* -maxdepth 2 -type f); do \
B=$$(basename $$F); \
if ! grep -q $$B $@; then \
FAIL=1; \
if ! grep -q '^\.so' $$F; then \
echo stray manpage not added to index: $$F; \
else \
echo manpage alias not added to index: $$F; \
fi; \
fi; \
done; \
if [ $$FAIL -ne 0 ]; then exit 1; fi
mkdir -p $(DOC_OUT_HTML)/en/man/man/images/
find $(DOC_MAN) -maxdepth 3 -name "*.png" ! -name "grohtml*" -exec mv {} "$(DOC_OUT_HTML)/en/man/man/images/" \;
# Translated landing pages append the same manpage index English uses; every
# language now renders the full manpage set, so the relative man/ hrefs all
# resolve under <lang>/man/ (no dead links).
$(DOC_OUT_HTML)/%/index.html: $(DOC_OUT_ADOC)/%/index.tmpl objects/index.incl ../VERSION $(DOC_SRCDIR)/index.foot $(DOC_SRCDIR)/docinfo-header.html $(DOC_SRCDIR)/Submakefile
@mkdir -p $(dir $@)
$(Q){ \
topbar=$$(mktemp) ; \
sed -e 's|{lcnc-cssrel}|../|g' -e 's|{lcnc-lang-label}|$(LANG_LABEL_$*)|g' -e 's|{lcnc-subpath}|index.html|g' $(DOC_SRCDIR)/docinfo-header.html > $$topbar ; \
(cat $(DOC_OUT_ADOC)/$*/index.tmpl objects/index.incl $(DOC_SRCDIR)/index.foot) \
| sed "s/@VERSION@/`cat ../VERSION`/" \
| awk -v t="$$topbar" 'BEGIN{ while ((getline line < t) > 0) buf = buf line "\n" } /<body>/{ print; printf "%s", buf; next } { print }' \
| sed 's|href="lcnc-overrides.css"|href="../lcnc-overrides.css"|g' \
| if [ "yes" != "$(BUILD_DOCS_TRANSLATED)" ]; then sed '/@TRANSLATIONS@/,/@ENDTRANSLATIONS@/d' ; else grep -Ev '@(END)?TRANSLATIONS@'; fi > $@ ; \
rm -f $$topbar ; \
}
# Rich English landing page lives at <html>/en/index.html (sibling to the
# translated <html>/<lang>/index.html files), generated from index.tmpl
# plus the manpage index include.
$(DOC_OUT_HTML)/en/index.html: $(DOC_SRCDIR)/index.tmpl objects/index.incl $(DOC_SRCDIR)/index.foot ../VERSION $(DOC_SRCDIR)/docinfo-header.html $(DOC_SRCDIR)/Submakefile
@mkdir -p $(dir $@)
$(Q){ \
topbar=$$(mktemp) ; \
sed 's|{lcnc-subpath}|index.html|g' objects/topbar-en.html > $$topbar ; \
(cat $(DOC_SRCDIR)/index.tmpl objects/index.incl $(DOC_SRCDIR)/index.foot) \
| sed "s/@VERSION@/`cat ../VERSION`/" \
| awk -v t="$$topbar" 'BEGIN{ while ((getline line < t) > 0) buf = buf line "\n" } /<body>/{ print; printf "%s", buf; next } { print }' \
| sed 's|href="lcnc-overrides.css"|href="../lcnc-overrides.css"|g' \
| if [ "yes" != "$(BUILD_DOCS_TRANSLATED)" ]; then sed '/@TRANSLATIONS@/,/@ENDTRANSLATIONS@/d' ; else grep -Ev '@(END)?TRANSLATIONS@'; fi > $@ ; \
rm -f $$topbar ; \
}
# Tree-root redirect: docs/build/html/index.html bounces to en/. The
# page is a checked-in static file under docs/src/; just copy it.
$(DOC_OUT_HTML)/index.html: $(DOC_SRCDIR)/redirect-index.html
@mkdir -p $(dir $@)
@cp -f $< $@
# PDF rule. Uses asciidoctor-pdf with our xref + image preprocessor
# extensions and the LinuxCNC theme. The xref-root attribute is set
# to the directory of the source file so each language tree gets
# its own anchor index (Master_*.adoc under DOC_SRCDIR for English,
# DOC_SRCDIR/<lang>/Master_*.adoc for translations).
DOC_FONT_DIR = objects/.fonts
# Locations of the NotoSerifCJK Regular / Bold TrueType Collections are
# discovered by configure (via fontconfig with package-path fallbacks)
# and exported through Makefile.inc, so the build works on any distro
# that ships the .ttc, not only the Debian path.
CJK_TTFS = $(DOC_FONT_DIR)/NotoSerifCJKsc-Regular.ttf \
$(DOC_FONT_DIR)/NotoSerifCJKsc-Bold.ttf
# DejaVu Sans Mono is the code-block font. asciidoctor-pdf only looks for
# fonts in pdf-fontsdir by filename, not via fontconfig, so discover the
# files at build time and symlink them into DOC_FONT_DIR. Keeps the
# pdf-theme.yml portable (no absolute paths baked in).
DEJAVU_TTFS = $(DOC_FONT_DIR)/DejaVuSansMono.ttf \
$(DOC_FONT_DIR)/DejaVuSansMono-Bold.ttf \
$(DOC_FONT_DIR)/DejaVuSansMono-Oblique.ttf \
$(DOC_FONT_DIR)/DejaVuSansMono-BoldOblique.ttf
DOC_FONTS = $(CJK_TTFS) $(DEJAVU_TTFS)
# Build a TrueType (glyf-based) copy of the Simplified Chinese cut of the
# Noto Serif CJK font. The system font is shipped only as a CFF/OTF
# TrueType Collection and prawn 2.4 corrupts the PDF when asked to embed
# CFF outlines, so we convert the curves with cu2qu first. This is the
# fallback font that lets the translated docs render their few non-Latin
# characters; the base text is still served by Noto Serif.
# Scan both docs/src and the po catalogues: the .po files hold the full
# translated text, so the glyph subset no longer depends on whether po4a
# has produced the per-language .adoc yet. Without this the subset can
# come up empty on a fresh build and the converter falls back to the full
# ~65k-glyph face (~200s vs ~1.5s).
$(DOC_FONT_DIR)/NotoSerifCJKsc-Regular.ttf: $(NOTOCJK_REGULAR_TTC) $(DOC_SRCDIR)/otf2ttf.py $(wildcard $(DOC_DIR)/po/*.po)
@mkdir -p $(DOC_FONT_DIR)
$(ECHO) "Converting CJK font to TTF: $(notdir $@)"
$(Q)python3 $(DOC_SRCDIR)/otf2ttf.py --ttc-index 2 --text-from $(DOC_SRCDIR) --text-from $(DOC_DIR)/po $< $@.tmp && mv $@.tmp $@
$(DOC_FONT_DIR)/NotoSerifCJKsc-Bold.ttf: $(NOTOCJK_BOLD_TTC) $(DOC_SRCDIR)/otf2ttf.py $(wildcard $(DOC_DIR)/po/*.po)
@mkdir -p $(DOC_FONT_DIR)
$(ECHO) "Converting CJK font to TTF: $(notdir $@)"
$(Q)python3 $(DOC_SRCDIR)/otf2ttf.py --ttc-index 2 --text-from $(DOC_SRCDIR) --text-from $(DOC_DIR)/po $< $@.tmp && mv $@.tmp $@
# DejaVu Sans Mono discovery: fc-match resolves the canonical .ttf for
# each style, we symlink it into DOC_FONT_DIR under its expected filename
# so pdf-theme.yml can stay path-free.
define DEJAVU_RULE
$(DOC_FONT_DIR)/$(1): | $(DOC_FONT_DIR)
@F=$$$$(fc-match --format='%{file}' '$(2)' 2>/dev/null); \
if [ -z "$$$$F" ] || [ ! -r "$$$$F" ]; then \
echo "fc-match could not locate '$(2)'; install fonts-dejavu" >&2; \
exit 1; \
fi; \
ln -sf "$$$$F" $$@
endef
$(eval $(call DEJAVU_RULE,DejaVuSansMono.ttf,DejaVu Sans Mono:style=Book))
$(eval $(call DEJAVU_RULE,DejaVuSansMono-Bold.ttf,DejaVu Sans Mono:style=Bold))
$(eval $(call DEJAVU_RULE,DejaVuSansMono-Oblique.ttf,DejaVu Sans Mono:style=Oblique))
$(eval $(call DEJAVU_RULE,DejaVuSansMono-BoldOblique.ttf,DejaVu Sans Mono:style=Bold Oblique))
$(DOC_FONT_DIR):
@mkdir -p $@
# Master_*.adoc -> Master_*.pdf via asciidoctor-pdf. Two rules with
# identical bodies: English sources are read from $(DOC_SRCDIR) and need
# an xref-exclude that filters out the translated language subtrees;
# translated sources are read from $(DOC_OUT_ADOC)/<lang> (po4a output)
# and are rooted inside their own lang dir, so the exclude is empty.
# English PDFs are emitted into objects/ (build scratch) instead of beside
# the source; translated PDFs stay next to their po4a-generated source.
#
# svgs_made_from_dots is .PHONY; as a normal prereq it forces every PDF
# to rebuild on every run. Order-only (after the |) instead; the SVGs
# the PDF embeds are tracked via .adoc-images-stamp.
define ASCIIDOCTOR_PDF_RULE
$(4)/%.pdf: $(1)/%.adoc .adoc-images-stamp $$(DOC_FONTS) $$(DOC_EXTENSIONS) | svgs_made_from_dots stage-en
$$(ECHO) Building $$@
@mkdir -p $$(dir $$@)
@rm -f $$@ $$@.raw
$$(Q)timeout 900 asciidoctor-pdf \
-r $$(realpath $$(DOC_SRCDIR))/extensions/xref_resolver.rb \
-r $$(realpath $$(DOC_SRCDIR))/extensions/image_resolver.rb \
-r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_hal.rb \
-r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ngc.rb \
-r $$(realpath $$(DOC_SRCDIR))/extensions/rouge_ini.rb \
--sourcemap \
-a compat-mode \
-a "doc-languages=$$(LANGUAGES)" \
-a "lcnc-lang=$3" \
-a xref-root=$$(dir $$<) \
-a "xref-exclude=$(2)" \
-a "scriptdir=$$(DOC_SRCDIR)/" \
-a "pdf-fontsdir=$$(realpath $$(DOC_FONT_DIR));GEM_FONTS_DIR" \
-a "lversion=$$(shell cat ../VERSION)" \