Fossil SCM

fossil-scm / autosetup / cc.tcl
Blame History Raw 757 lines
1
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
2
# All rights reserved
3
4
# @synopsis:
5
#
6
# The 'cc' module supports checking various 'features' of the C or C++
7
# compiler/linker environment. Common commands are 'cc-check-includes',
8
# 'cc-check-types', 'cc-check-functions', 'cc-with' and 'make-config-header'
9
#
10
# The following environment variables are used if set:
11
#
12
## CC - C compiler
13
## CXX - C++ compiler
14
## CPP - C preprocessor
15
## CCACHE - Set to "none" to disable automatic use of ccache
16
## CPPFLAGS - Additional C preprocessor compiler flags (C and C++), before CFLAGS, CXXFLAGS
17
## CFLAGS - Additional C compiler flags
18
## CXXFLAGS - Additional C++ compiler flags
19
## LDFLAGS - Additional compiler flags during linking
20
## LINKFLAGS - ?How is this different from LDFLAGS?
21
## LIBS - Additional libraries to use (for all tests)
22
## CROSS - Tool prefix for cross compilation
23
#
24
# The following variables are defined from the corresponding
25
# environment variables if set.
26
#
27
## CC_FOR_BUILD
28
## LD
29
30
use system
31
32
options {}
33
34
# Checks for the existence of the given function by linking
35
#
36
proc cctest_function {function} {
37
cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
38
}
39
40
# Checks for the existence of the given type by compiling
41
proc cctest_type {type} {
42
cctest -code "$type _x;"
43
}
44
45
# Checks for the existence of the given type/structure member.
46
# e.g. "struct stat.st_mtime"
47
proc cctest_member {struct_member} {
48
# split at the first dot
49
regexp {^([^.]+)[.](.*)$} $struct_member -> struct member
50
cctest -code "static $struct _s; return sizeof(_s.$member);"
51
}
52
53
# Checks for the existence of the given define by compiling
54
#
55
proc cctest_define {name} {
56
cctest -code "#ifndef $name\n#error not defined\n#endif"
57
}
58
59
# Checks for the existence of the given name either as
60
# a macro (#define) or an rvalue (such as an enum)
61
#
62
proc cctest_decl {name} {
63
cctest -code "#ifndef $name\n(void)$name;\n#endif"
64
}
65
66
# @cc-check-sizeof type ...
67
#
68
# Checks the size of the given types (between 1 and 32, inclusive).
69
# Defines a variable with the size determined, or 'unknown' otherwise.
70
# e.g. for type 'long long', defines 'SIZEOF_LONG_LONG'.
71
# Returns the size of the last type.
72
#
73
proc cc-check-sizeof {args} {
74
foreach type $args {
75
msg-checking "Checking for sizeof $type..."
76
set size unknown
77
# Try the most common sizes first
78
foreach i {4 8 1 2 16 32} {
79
if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} {
80
set size $i
81
break
82
}
83
}
84
msg-result $size
85
set define [feature-define-name $type SIZEOF_]
86
define $define $size
87
}
88
# Return the last result
89
get-define $define
90
}
91
92
# Checks for each feature in $list by using the given script.
93
#
94
# When the script is evaluated, $each is set to the feature
95
# being checked, and $extra is set to any additional cctest args.
96
#
97
# Returns 1 if all features were found, or 0 otherwise.
98
proc cc-check-some-feature {list script} {
99
set ret 1
100
foreach each $list {
101
if {![check-feature $each $script]} {
102
set ret 0
103
}
104
}
105
return $ret
106
}
107
108
# @cc-check-includes includes ...
109
#
110
# Checks that the given include files can be used.
111
proc cc-check-includes {args} {
112
cc-check-some-feature $args {
113
set with {}
114
if {[dict exists $::autosetup(cc-include-deps) $each]} {
115
set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]]
116
msg-quiet cc-check-includes {*}$deps
117
foreach i $deps {
118
if {[have-feature $i]} {
119
lappend with $i
120
}
121
}
122
}
123
if {[llength $with]} {
124
cc-with [list -includes $with] {
125
cctest -includes $each
126
}
127
} else {
128
cctest -includes $each
129
}
130
}
131
}
132
133
# @cc-include-needs include required ...
134
#
135
# Ensures that when checking for '$include', a check is first
136
# made for each '$required' file, and if found, it is included with '#include'.
137
proc cc-include-needs {file args} {
138
foreach depfile $args {
139
dict set ::autosetup(cc-include-deps) $file $depfile 1
140
}
141
}
142
143
# @cc-check-types type ...
144
#
145
# Checks that the types exist.
146
proc cc-check-types {args} {
147
cc-check-some-feature $args {
148
cctest_type $each
149
}
150
}
151
152
# @cc-check-defines define ...
153
#
154
# Checks that the given preprocessor symbols are defined.
155
proc cc-check-defines {args} {
156
cc-check-some-feature $args {
157
cctest_define $each
158
}
159
}
160
161
# @cc-check-decls name ...
162
#
163
# Checks that each given name is either a preprocessor symbol or rvalue
164
# such as an enum. Note that the define used is 'HAVE_DECL_xxx'
165
# rather than 'HAVE_xxx'.
166
proc cc-check-decls {args} {
167
set ret 1
168
foreach name $args {
169
msg-checking "Checking for $name..."
170
set r [cctest_decl $name]
171
define-feature "decl $name" $r
172
if {$r} {
173
msg-result "ok"
174
} else {
175
msg-result "not found"
176
set ret 0
177
}
178
}
179
return $ret
180
}
181
182
# @cc-check-functions function ...
183
#
184
# Checks that the given functions exist (can be linked).
185
proc cc-check-functions {args} {
186
cc-check-some-feature $args {
187
cctest_function $each
188
}
189
}
190
191
# @cc-check-members type.member ...
192
#
193
# Checks that the given type/structure members exist.
194
# A structure member is of the form 'struct stat.st_mtime'.
195
proc cc-check-members {args} {
196
cc-check-some-feature $args {
197
cctest_member $each
198
}
199
}
200
201
# @cc-check-function-in-lib function libs ?otherlibs?
202
#
203
# Checks that the given function can be found in one of the libs.
204
#
205
# First checks for no library required, then checks each of the libraries
206
# in turn.
207
#
208
# If the function is found, the feature is defined and 'lib_$function' is defined
209
# to '-l$lib' where the function was found, or "" if no library required.
210
# In addition, '-l$lib' is prepended to the 'LIBS' define.
211
#
212
# If additional libraries may be needed for linking, they should be specified
213
# with '$extralibs' as '-lotherlib1 -lotherlib2'.
214
# These libraries are not automatically added to 'LIBS'.
215
#
216
# Returns 1 if found or 0 if not.
217
#
218
proc cc-check-function-in-lib {function libs {otherlibs {}}} {
219
msg-checking "Checking libs for $function..."
220
set found 0
221
cc-with [list -libs $otherlibs] {
222
if {[cctest_function $function]} {
223
msg-result "none needed"
224
define lib_$function ""
225
incr found
226
} else {
227
foreach lib $libs {
228
cc-with [list -libs -l$lib] {
229
if {[cctest_function $function]} {
230
msg-result -l$lib
231
define lib_$function -l$lib
232
# prepend to LIBS
233
define LIBS "-l$lib [get-define LIBS]"
234
incr found
235
break
236
}
237
}
238
}
239
}
240
}
241
define-feature $function $found
242
if {!$found} {
243
msg-result "no"
244
}
245
return $found
246
}
247
248
# @cc-check-tools tool ...
249
#
250
# Checks for existence of the given compiler tools, taking
251
# into account any cross compilation prefix.
252
#
253
# For example, when checking for 'ar', first 'AR' is checked on the command
254
# line and then in the environment. If not found, '${host}-ar' or
255
# simply 'ar' is assumed depending upon whether cross compiling.
256
# The path is searched for this executable, and if found 'AR' is defined
257
# to the executable name.
258
# Note that even when cross compiling, the simple 'ar' is used as a fallback,
259
# but a warning is generated. This is necessary for some toolchains.
260
#
261
# It is an error if the executable is not found.
262
#
263
proc cc-check-tools {args} {
264
foreach tool $args {
265
set TOOL [string toupper $tool]
266
set exe [get-env $TOOL [get-define cross]$tool]
267
if {[find-executable $exe]} {
268
define $TOOL $exe
269
continue
270
}
271
if {[find-executable $tool]} {
272
msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect"
273
define $TOOL $tool
274
continue
275
}
276
user-error "Failed to find $exe"
277
}
278
}
279
280
# @cc-check-progs prog ...
281
#
282
# Checks for existence of the given executables on the path.
283
#
284
# For example, when checking for 'grep', the path is searched for
285
# the executable, 'grep', and if found 'GREP' is defined as 'grep'.
286
#
287
# If the executable is not found, the variable is defined as 'false'.
288
# Returns 1 if all programs were found, or 0 otherwise.
289
#
290
proc cc-check-progs {args} {
291
set failed 0
292
foreach prog $args {
293
set PROG [string toupper $prog]
294
msg-checking "Checking for $prog..."
295
if {![find-executable $prog]} {
296
msg-result no
297
define $PROG false
298
incr failed
299
} else {
300
msg-result ok
301
define $PROG $prog
302
}
303
}
304
expr {!$failed}
305
}
306
307
# @cc-path-progs prog ...
308
#
309
# Like cc-check-progs, but sets the define to the full path rather
310
# than just the program name.
311
#
312
proc cc-path-progs {args} {
313
set failed 0
314
foreach prog $args {
315
set PROG [string toupper $prog]
316
msg-checking "Checking for $prog..."
317
set path [find-executable-path $prog]
318
if {$path eq ""} {
319
msg-result no
320
define $PROG false
321
incr failed
322
} else {
323
msg-result $path
324
define $PROG $path
325
}
326
}
327
expr {!$failed}
328
}
329
330
# Adds the given settings to $::autosetup(ccsettings) and
331
# returns the old settings.
332
#
333
proc cc-add-settings {settings} {
334
if {[llength $settings] % 2} {
335
autosetup-error "settings list is missing a value: $settings"
336
}
337
338
set prev [cc-get-settings]
339
# workaround a bug in some versions of jimsh by forcing
340
# conversion of $prev to a list
341
llength $prev
342
343
array set new $prev
344
345
foreach {name value} $settings {
346
switch -exact -- $name {
347
-cflags - -includes {
348
# These are given as lists
349
lappend new($name) {*}[list-non-empty $value]
350
}
351
-declare {
352
lappend new($name) $value
353
}
354
-libs {
355
# Note that new libraries are added before previous libraries
356
set new($name) [list {*}[list-non-empty $value] {*}$new($name)]
357
}
358
-link - -lang - -nooutput {
359
set new($name) $value
360
}
361
-source - -sourcefile - -code {
362
# XXX: These probably are only valid directly from cctest
363
set new($name) $value
364
}
365
default {
366
autosetup-error "unknown cctest setting: $name"
367
}
368
}
369
}
370
371
cc-store-settings [array get new]
372
373
return $prev
374
}
375
376
proc cc-store-settings {new} {
377
set ::autosetup(ccsettings) $new
378
}
379
380
proc cc-get-settings {} {
381
return $::autosetup(ccsettings)
382
}
383
384
# Similar to cc-add-settings, but each given setting
385
# simply replaces the existing value.
386
#
387
# Returns the previous settings
388
proc cc-update-settings {args} {
389
set prev [cc-get-settings]
390
cc-store-settings [dict merge $prev $args]
391
return $prev
392
}
393
394
# @cc-with settings ?{ script }?
395
#
396
# Sets the given 'cctest' settings and then runs the tests in '$script'.
397
# Note that settings such as '-lang' replace the current setting, while
398
# those such as '-includes' are appended to the existing setting.
399
#
400
# If no script is given, the settings become the default for the remainder
401
# of the 'auto.def' file.
402
#
403
## cc-with {-lang c++} {
404
## # This will check with the C++ compiler
405
## cc-check-types bool
406
## cc-with {-includes signal.h} {
407
## # This will check with the C++ compiler, signal.h and any existing includes.
408
## ...
409
## }
410
## # back to just the C++ compiler
411
## }
412
#
413
# The '-libs' setting is special in that newer values are added *before* earlier ones.
414
#
415
## cc-with {-libs {-lc -lm}} {
416
## cc-with {-libs -ldl} {
417
## cctest -libs -lsocket ...
418
## # libs will be in this order: -lsocket -ldl -lc -lm
419
## }
420
## }
421
#
422
# If you wish to invoke something like cc-check-flags but not have -cflags updated,
423
# use the following idiom:
424
#
425
## cc-with {} {
426
## cc-check-flags ...
427
## }
428
proc cc-with {settings args} {
429
if {[llength $args] == 0} {
430
cc-add-settings $settings
431
} elseif {[llength $args] > 1} {
432
autosetup-error "usage: cc-with settings ?script?"
433
} else {
434
set save [cc-add-settings $settings]
435
set rc [catch {uplevel 1 [lindex $args 0]} result info]
436
cc-store-settings $save
437
if {$rc != 0} {
438
return -code [dict get $info -code] $result
439
}
440
return $result
441
}
442
}
443
444
# @cctest ?settings?
445
#
446
# Low level C/C++ compiler checker. Compiles and or links a small C program
447
# according to the arguments and returns 1 if OK, or 0 if not.
448
#
449
# Supported settings are:
450
#
451
## -cflags cflags A list of flags to pass to the compiler
452
## -includes list A list of includes, e.g. {stdlib.h stdio.h}
453
## -declare code Code to declare before main()
454
## -link 1 Don't just compile, link too
455
## -lang c|c++ Use the C (default) or C++ compiler
456
## -libs liblist List of libraries to link, e.g. {-ldl -lm}
457
## -code code Code to compile in the body of main()
458
## -source code Compile a complete program. Ignore -includes, -declare and -code
459
## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file]
460
## -nooutput 1 Treat any compiler output (e.g. a warning) as an error
461
#
462
# Unless '-source' or '-sourcefile' is specified, the C program looks like:
463
#
464
## #include <firstinclude> /* same for remaining includes in the list */
465
## declare-code /* any code in -declare, verbatim */
466
## int main(void) {
467
## code /* any code in -code, verbatim */
468
## return 0;
469
## }
470
#
471
# And the command line looks like:
472
#
473
## CC -cflags CFLAGS CPPFLAGS conftest.c -o conftest.o
474
## CXX -cflags CXXFLAGS CPPFLAGS conftest.cpp -o conftest.o
475
#
476
# And if linking:
477
#
478
## CC LDFLAGS -cflags CFLAGS conftest.c -o conftest -libs LIBS
479
## CXX LDFLAGS -cflags CXXFLAGS conftest.c -o conftest -libs LIBS
480
#
481
# Any failures are recorded in 'config.log'
482
#
483
proc cctest {args} {
484
set tmp conftest__
485
486
# Easiest way to merge in the settings
487
cc-with $args {
488
array set opts [cc-get-settings]
489
}
490
491
if {[info exists opts(-sourcefile)]} {
492
set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"]
493
}
494
if {[info exists opts(-source)]} {
495
set lines $opts(-source)
496
} else {
497
foreach i $opts(-includes) {
498
if {$opts(-code) ne "" && ![feature-checked $i]} {
499
# Compiling real code with an unchecked header file
500
# Quickly (and silently) check for it now
501
502
# Remove all -includes from settings before checking
503
set saveopts [cc-update-settings -includes {}]
504
msg-quiet cc-check-includes $i
505
cc-store-settings $saveopts
506
}
507
if {$opts(-code) eq "" || [have-feature $i]} {
508
lappend source "#include <$i>"
509
}
510
}
511
lappend source {*}$opts(-declare)
512
lappend source "int main(void) {"
513
lappend source $opts(-code)
514
lappend source "return 0;"
515
lappend source "}"
516
517
set lines [join $source \n]
518
}
519
520
# Build the command line
521
set cmdline {}
522
lappend cmdline {*}[get-define CCACHE]
523
switch -exact -- $opts(-lang) {
524
c++ {
525
set src conftest__.cpp
526
lappend cmdline {*}[get-define CXX]
527
set cflags [get-define CXXFLAGS]
528
}
529
c {
530
set src conftest__.c
531
lappend cmdline {*}[get-define CC]
532
set cflags [get-define CFLAGS]
533
}
534
default {
535
autosetup-error "cctest called with unknown language: $opts(-lang)"
536
}
537
}
538
539
if {$opts(-link)} {
540
lappend cmdline {*}[get-define LDFLAGS]
541
} else {
542
lappend cflags {*}[get-define CPPFLAGS]
543
set tmp conftest__.o
544
lappend cmdline -c
545
}
546
lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""] {*}$cflags
547
lappend cmdline $src -o $tmp
548
if {$opts(-link)} {
549
lappend cmdline {*}$opts(-libs) {*}[get-define LIBS]
550
}
551
552
# At this point we have the complete command line and the
553
# complete source to be compiled. Get the result from cache if
554
# we can
555
if {[info exists ::cc_cache($cmdline,$lines)]} {
556
msg-checking "(cached) "
557
set ok $::cc_cache($cmdline,$lines)
558
if {$::autosetup(debug)} {
559
configlog "From cache (ok=$ok): [join $cmdline]"
560
configlog "============"
561
configlog $lines
562
configlog "============"
563
}
564
return $ok
565
}
566
567
writefile $src $lines\n
568
569
set ok 1
570
set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
571
if {$err || ($opts(-nooutput) && [string length $result])} {
572
configlog "Failed: [join $cmdline]"
573
configlog $result
574
configlog "============"
575
configlog "The failed code was:"
576
configlog $lines
577
configlog "============"
578
set ok 0
579
} elseif {$::autosetup(debug)} {
580
configlog "Compiled OK: [join $cmdline]"
581
configlog "============"
582
configlog $lines
583
configlog "============"
584
}
585
file delete $src
586
file delete $tmp
587
588
# cache it
589
set ::cc_cache($cmdline,$lines) $ok
590
591
return $ok
592
}
593
594
# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
595
#
596
# Deprecated - see 'make-config-header'
597
proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} {
598
user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead"
599
make-config-header $file -auto $autopatterns -bare $barepatterns
600
}
601
602
# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ...
603
#
604
# Examines all defined variables which match the given patterns
605
# and writes an include file, '$file', which defines each of these.
606
# Variables which match '-auto' are output as follows:
607
# - defines which have the value '0' are ignored.
608
# - defines which have integer values are defined as the integer value.
609
# - any other value is defined as a string, e.g. '"value"'
610
# Variables which match '-bare' are defined as-is.
611
# Variables which match '-str' are defined as a string, e.g. '"value"'
612
# Variables which match '-none' are omitted.
613
#
614
# Note that order is important. The first pattern that matches is selected.
615
# Default behaviour is:
616
#
617
## -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
618
#
619
# If the file would be unchanged, it is not written.
620
proc make-config-header {file args} {
621
set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]]
622
file mkdir [file dirname $file]
623
set lines {}
624
lappend lines "#ifndef $guard"
625
lappend lines "#define $guard"
626
627
# Add some defaults
628
lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*
629
630
foreach n [lsort [dict keys [all-defines]]] {
631
set value [get-define $n]
632
set type [calc-define-output-type $n $args]
633
switch -exact -- $type {
634
-bare {
635
# Just output the value unchanged
636
}
637
-none {
638
continue
639
}
640
-str {
641
set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
642
}
643
-auto {
644
# Automatically determine the type
645
if {$value eq "0"} {
646
lappend lines "/* #undef $n */"
647
continue
648
}
649
if {![string is integer -strict $value]} {
650
set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
651
}
652
}
653
"" {
654
continue
655
}
656
default {
657
autosetup-error "Unknown type in make-config-header: $type"
658
}
659
}
660
lappend lines "#define $n $value"
661
}
662
lappend lines "#endif"
663
set buf [join $lines \n]
664
write-if-changed $file $buf {
665
msg-result "Created $file"
666
}
667
}
668
669
proc calc-define-output-type {name spec} {
670
foreach {type patterns} $spec {
671
foreach pattern $patterns {
672
if {[string match $pattern $name]} {
673
return $type
674
}
675
}
676
}
677
return ""
678
}
679
680
# Initialise some values from the environment or commandline or default settings
681
foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS CFLAGS} {
682
lassign $i var default
683
define $var [get-env $var $default]
684
}
685
686
if {[env-is-set CC]} {
687
# Set by the user, so don't try anything else
688
set try [list [get-env CC ""]]
689
} else {
690
# Try some reasonable options
691
set try [list [get-define cross]cc [get-define cross]gcc]
692
}
693
define CC [find-an-executable {*}$try]
694
if {[get-define CC] eq ""} {
695
user-error "Could not find a C compiler. Tried: [join $try ", "]"
696
}
697
698
define CPP [get-env CPP "[get-define CC] -E"]
699
700
# XXX: Could avoid looking for a C++ compiler until requested
701
# If CXX isn't found, it is set to the empty string.
702
if {[env-is-set CXX]} {
703
define CXX [find-an-executable -required [get-env CXX ""]]
704
} else {
705
define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++]
706
}
707
708
# CXXFLAGS default to CFLAGS if not specified
709
define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]
710
711
# May need a CC_FOR_BUILD, so look for one
712
define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false]
713
714
if {[get-define CC] eq ""} {
715
user-error "Could not find a C compiler. Tried: [join $try ", "]"
716
}
717
718
# These start empty and never come from the user or environment
719
define AS_CFLAGS ""
720
define AS_CPPFLAGS ""
721
define AS_CXXFLAGS ""
722
723
define CCACHE [find-an-executable [get-env CCACHE ccache]]
724
725
# If any of these are set in the environment, propagate them to the AUTOREMAKE commandline
726
foreach i {CC CXX CCACHE CPP CFLAGS CXXFLAGS CXXFLAGS LDFLAGS LIBS CROSS CPPFLAGS LINKFLAGS CC_FOR_BUILD LD} {
727
if {[env-is-set $i]} {
728
# Note: If the variable is set on the command line, get-env will return that value
729
# so the command line will continue to override the environment
730
define-append-argv AUTOREMAKE $i=[get-env $i ""]
731
}
732
}
733
734
# Initial cctest settings
735
cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0}
736
set autosetup(cc-include-deps) {}
737
738
msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS] [get-define CPPFLAGS]"
739
if {[get-define CXX] ne "false"} {
740
msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS] [get-define CPPFLAGS]"
741
}
742
msg-result "Build C compiler...[get-define CC_FOR_BUILD]"
743
744
# On Darwin, we prefer to use -g0 to avoid creating .dSYM directories
745
# but some compilers may not support it, so test here.
746
switch -glob -- [get-define host] {
747
*-*-darwin* {
748
if {[cctest -cflags {-g0}]} {
749
define cc-default-debug -g0
750
}
751
}
752
}
753
754
if {![cc-check-includes stdlib.h]} {
755
user-error "Compiler does not work. See config.log"
756
}
757

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button