Index: llvm/trunk/cmake/config-ix.cmake =================================================================== --- llvm/trunk/cmake/config-ix.cmake +++ llvm/trunk/cmake/config-ix.cmake @@ -628,3 +628,34 @@ endif() string(REPLACE " " ";" LLVM_BINDINGS_LIST "${LLVM_BINDINGS}") + +function(find_python_module module) + string(TOUPPER ${module} module_upper) + set(FOUND_VAR PY_${module_upper}_FOUND) + + execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" "import ${module}" + RESULT_VARIABLE status + ERROR_QUIET) + + if(status) + set(${FOUND_VAR} 0 PARENT_SCOPE) + message(STATUS "Could NOT find Python module ${module}") + else() + set(${FOUND_VAR} 1 PARENT_SCOPE) + message(STATUS "Found Python module ${module}") + endif() +endfunction() + +set (PYTHON_MODULES + pygments + yaml + ) +foreach(module ${PYTHON_MODULES}) + find_python_module(${module}) +endforeach() + +if(PY_PYGMENTS_FOUND AND PY_YAML_FOUND) + set (LLVM_HAVE_OPT_VIEWER_MODULES 1) +else() + set (LLVM_HAVE_OPT_VIEWER_MODULES 0) +endif() Index: llvm/trunk/test/lit.cfg.py =================================================================== --- llvm/trunk/test/lit.cfg.py +++ llvm/trunk/test/lit.cfg.py @@ -123,6 +123,7 @@ ocamlopt_command = '%s ocamlopt -cclib -L%s -cclib -Wl,-rpath,%s %s' % ( config.ocamlfind_executable, config.llvm_lib_dir, config.llvm_lib_dir, config.ocaml_flags) +opt_viewer_cmd = '%s %s/tools/opt-viewer/opt-viewer.py' % (sys.executable, config.llvm_src_root) tools = [ ToolSubst('%lli', FindTool('lli'), post='.', extra_args=lli_args), @@ -132,6 +133,7 @@ ToolSubst('%ld64', ld64_cmd, unresolved='ignore'), ToolSubst('%ocamlc', ocamlc_command, unresolved='ignore'), ToolSubst('%ocamlopt', ocamlopt_command, unresolved='ignore'), + ToolSubst('%opt-viewer', opt_viewer_cmd), ] # FIXME: Why do we have both `lli` and `%lli` that do slightly different things? @@ -286,3 +288,6 @@ if config.llvm_libxml2_enabled == '1': config.available_features.add('libxml2') + +if config.have_opt_viewer_modules: + config.available_features.add('have_opt_viewer_modules') Index: llvm/trunk/test/lit.site.cfg.py.in =================================================================== --- llvm/trunk/test/lit.site.cfg.py.in +++ llvm/trunk/test/lit.site.cfg.py.in @@ -43,6 +43,7 @@ config.llvm_libxml2_enabled = "@LLVM_LIBXML2_ENABLED@" config.llvm_host_triple = '@LLVM_HOST_TRIPLE@' config.host_arch = "@HOST_ARCH@" +config.have_opt_viewer_modules = @LLVM_HAVE_OPT_VIEWER_MODULES@ # Support substitution of the tools_dir with user parameters. This is # used when we can't determine the tool dir at configuration time. Index: llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.h =================================================================== --- llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.h +++ llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.h @@ -0,0 +1,16 @@ +void TestH(int *res, int *c, int *d, int *p, int n) { + int i; + +#pragma clang loop vectorize(assume_safety) + for (i = 0; i < 1600; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + for (i = 0; i < 16; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + foo(); + + foo(); bar(); foo(); +} Index: llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.c =================================================================== --- llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.c +++ llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.c @@ -0,0 +1,21 @@ +void bar(); +void foo() { bar(); } + +#include "or.h" + +void Test(int *res, int *c, int *d, int *p, int n) { + int i; + +#pragma clang loop vectorize(assume_safety) + for (i = 0; i < 1600; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + for (i = 0; i < 16; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + foo(); + + foo(); bar(); foo(); +} Index: llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.yaml =================================================================== --- llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.yaml +++ llvm/trunk/test/tools/opt-viewer/Inputs/basic/or.yaml @@ -0,0 +1,227 @@ +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: basic/or.c, Line: 2, Column: 14 } +Function: foo +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: foo + - String: ' because its definition is unavailable' +... +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: basic/or.h, Line: 15, Column: 10 } +Function: TestH +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: TestH + - String: ' because its definition is unavailable' +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: basic/or.h, Line: 13, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: TestH + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: basic/or.h, Line: 13, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: TestH +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: basic/or.h, Line: 15, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: TestH + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: basic/or.h, Line: 15, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: TestH +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: basic/or.h, Line: 15, Column: 17 } +Function: TestH +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: TestH + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: basic/or.h, Line: 15, Column: 17 } +Function: TestH +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: TestH +... +--- !Passed +Pass: loop-unroll +Name: FullyUnrolled +DebugLoc: { File: basic/or.h, Line: 9, Column: 3 } +Function: TestH +Args: + - String: 'completely unrolled loop with ' + - UnrollCount: '16' + - String: ' iterations' +... +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: basic/or.c, Line: 20, Column: 10 } +Function: Test +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: Test + - String: ' because its definition is unavailable' +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: basic/or.c, Line: 18, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: Test + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: basic/or.c, Line: 18, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: Test +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: basic/or.c, Line: 20, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: Test + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: basic/or.c, Line: 20, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: Test +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: basic/or.c, Line: 20, Column: 17 } +Function: Test +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: Test + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: basic/or.c, Line: 20, Column: 17 } +Function: Test +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: Test +... +--- !Passed +Pass: loop-unroll +Name: FullyUnrolled +DebugLoc: { File: basic/or.c, Line: 14, Column: 3 } +Function: Test +Args: + - String: 'completely unrolled loop with ' + - UnrollCount: '16' + - String: ' iterations' +... +--- !Passed +Pass: loop-vectorize +Name: Vectorized +DebugLoc: { File: basic/or.h, Line: 5, Column: 3 } +Function: TestH +Args: + - String: 'vectorized loop (vectorization width: ' + - VectorizationFactor: '4' + - String: ', interleaved count: ' + - InterleaveCount: '2' + - String: ')' +... +--- !Passed +Pass: loop-vectorize +Name: Vectorized +DebugLoc: { File: basic/or.c, Line: 10, Column: 3 } +Function: Test +Args: + - String: 'vectorized loop (vectorization width: ' + - VectorizationFactor: '4' + - String: ', interleaved count: ' + - InterleaveCount: '2' + - String: ')' +... Index: llvm/trunk/test/tools/opt-viewer/Outputs/basic/basic_or.h.html =================================================================== --- llvm/trunk/test/tools/opt-viewer/Outputs/basic/basic_or.h.html +++ llvm/trunk/test/tools/opt-viewer/Outputs/basic/basic_or.h.html @@ -0,0 +1,221 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line +Hotness +Optimization +Source +Inline Context +
1
void TestH(int *res, int *c, int *d, int *p, int n) {
2
  int i;
3
4
#pragma clang loop vectorize(assume_safety)
5
  for (i = 0; i < 1600; i++) {
loop-vectorize
  
vectorized loop (vectorization width: 4, interleaved count: 2) 
TestH
6
    res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
7
  }
8
9
  for (i = 0; i < 16; i++) {
loop-unroll
  
completely unrolled loop with 16 iterations 
TestH
10
    res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
11
  }
12
13
  foo();
inline
  
foo can be inlined into TestH with cost=30 (threshold=412) 
TestH
inline
  
foo inlined into TestH 
TestH
14
15
  foo(); bar(); foo();
inline
         
bar will not be inlined into TestH because its definition is unavailable 
TestH
inline
  
foo can be inlined into TestH with cost=30 (threshold=412) 
TestH
inline
  
foo inlined into TestH 
TestH
inline
                
foo can be inlined into TestH with cost=30 (threshold=412) 
TestH
inline
                
foo inlined into TestH 
TestH
16
}
17
18
+ + Index: llvm/trunk/test/tools/opt-viewer/Outputs/basic/basic_or.c.html =================================================================== --- llvm/trunk/test/tools/opt-viewer/Outputs/basic/basic_or.c.html +++ llvm/trunk/test/tools/opt-viewer/Outputs/basic/basic_or.c.html @@ -0,0 +1,264 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Line +Hotness +Optimization +Source +Inline Context +
1
void bar();
2
void foo() { bar(); }
inline
             
bar will not be inlined into foo because its definition is unavailable 
foo
3
4
#include "or.h"
5
6
void Test(int *res, int *c, int *d, int *p, int n) {
7
  int i;
8
9
#pragma clang loop vectorize(assume_safety)
10
  for (i = 0; i < 1600; i++) {
loop-vectorize
  
vectorized loop (vectorization width: 4, interleaved count: 2) 
Test
11
    res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
12
  }
13
14
  for (i = 0; i < 16; i++) {
loop-unroll
  
completely unrolled loop with 16 iterations 
Test
15
    res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
16
  }
17
18
  foo();
inline
  
foo can be inlined into Test with cost=30 (threshold=412) 
Test
inline
  
foo inlined into Test 
Test
19
20
  foo(); bar(); foo();
inline
         
bar will not be inlined into Test because its definition is unavailable 
Test
inline
  
foo can be inlined into Test with cost=30 (threshold=412) 
Test
inline
  
foo inlined into Test 
Test
inline
                
foo can be inlined into Test with cost=30 (threshold=412) 
Test
inline
                
foo inlined into Test 
Test
21
}
22
23
+ + Index: llvm/trunk/test/tools/opt-viewer/Outputs/basic/index.html =================================================================== --- llvm/trunk/test/tools/opt-viewer/Outputs/basic/index.html +++ llvm/trunk/test/tools/opt-viewer/Outputs/basic/index.html @@ -0,0 +1,151 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source LocationHotnessFunctionPass
basic/or.c:2:14fooinline
basic/or.c:10:3Testloop-vectorize
basic/or.c:14:3Testloop-unroll
basic/or.c:18:3Testinline
basic/or.c:18:3Testinline
basic/or.c:20:3Testinline
basic/or.c:20:3Testinline
basic/or.c:20:10Testinline
basic/or.c:20:17Testinline
basic/or.c:20:17Testinline
basic/or.h:5:3TestHloop-vectorize
basic/or.h:9:3TestHloop-unroll
basic/or.h:13:3TestHinline
basic/or.h:13:3TestHinline
basic/or.h:15:3TestHinline
basic/or.h:15:3TestHinline
basic/or.h:15:10TestHinline
basic/or.h:15:17TestHinline
basic/or.h:15:17TestHinline
+ + Index: llvm/trunk/test/tools/opt-viewer/Outputs/basic/style.css =================================================================== --- llvm/trunk/test/tools/opt-viewer/Outputs/basic/style.css +++ llvm/trunk/test/tools/opt-viewer/Outputs/basic/style.css @@ -0,0 +1,208 @@ +.source { + table-layout: fixed; + width: 100%; + white-space: nowrap; +} +.source td { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.red { + background-color: #ffd0d0; +} +.cyan { + background-color: cyan; +} +body { + font-family: -apple-system, sans-serif; +} +pre { + margin-top: 0px !important; + margin-bottom: 0px !important; +} +.source-name-title { + padding: 5px 10px; + border-bottom: 1px solid #dbdbdb; + background-color: #eee; + line-height: 35px; +} +.centered { + display: table; + margin-left: left; + margin-right: auto; + border: 1px solid #dbdbdb; + border-radius: 3px; +} +.expansion-view { + background-color: rgba(0, 0, 0, 0); + margin-left: 0px; + margin-top: 5px; + margin-right: 5px; + margin-bottom: 5px; + border: 1px solid #dbdbdb; + border-radius: 3px; +} +table { + border-collapse: collapse; +} +.light-row { + background: #ffffff; + border: 1px solid #dbdbdb; +} +.column-entry { + text-align: right; +} +.column-entry-left { + text-align: left; +} +.column-entry-white { + text-align: right; + background-color: #ffffff; +} +.column-entry-red { + text-align: right; + background-color: #ffd0d0; +} +.column-entry-green { + text-align: right; + background-color: #d0ffd0; +} +.column-entry-yellow { + text-align: left; + background-color: #ffe1a6; +} +.column-entry-0 { + background-color: #ffffff; +} +.column-entry-1 { + background-color: #eeeeee; +} +.line-number { + text-align: right; + color: #aaa; +} +.covered-line { + text-align: right; + color: #0080ff; +} +.uncovered-line { + text-align: right; + color: #ff3300; +} +.tooltip { + position: relative; + display: inline; + background-color: #b3e6ff; + text-decoration: none; +} +.tooltip span.tooltip-content { + position: absolute; + width: 100px; + margin-left: -50px; + color: #FFFFFF; + background: #000000; + height: 30px; + line-height: 30px; + text-align: center; + visibility: hidden; + border-radius: 6px; +} +.tooltip span.tooltip-content:after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -8px; + width: 0; height: 0; + border-top: 8px solid #000000; + border-right: 8px solid transparent; + border-left: 8px solid transparent; +} +:hover.tooltip span.tooltip-content { + visibility: visible; + opacity: 0.8; + bottom: 30px; + left: 50%; + z-index: 999; +} +th, td { + vertical-align: top; + padding: 2px 5px; + border-collapse: collapse; + border-right: solid 1px #eee; + border-left: solid 1px #eee; +} +td:first-child { + border-left: none; +} +td:last-child { + border-right: none; +} + +/* Generated with pygmentize -S colorful -f html >> style.css */ + +.hll { background-color: #ffffcc } +.c { color: #888888 } /* Comment */ +.err { color: #FF0000; background-color: #FFAAAA } /* Error */ +.k { color: #008800; font-weight: bold } /* Keyword */ +.o { color: #333333 } /* Operator */ +.ch { color: #888888 } /* Comment.Hashbang */ +.cm { color: #888888 } /* Comment.Multiline */ +.cp { color: #557799 } /* Comment.Preproc */ +.cpf { color: #888888 } /* Comment.PreprocFile */ +.c1 { color: #888888 } /* Comment.Single */ +.cs { color: #cc0000; font-weight: bold } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #FF0000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #888888 } /* Generic.Output */ +.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0044DD } /* Generic.Traceback */ +.kc { color: #008800; font-weight: bold } /* Keyword.Constant */ +.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */ +.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #333399; font-weight: bold } /* Keyword.Type */ +.m { color: #6600EE; font-weight: bold } /* Literal.Number */ +.s { background-color: #fff0f0 } /* Literal.String */ +.na { color: #0000CC } /* Name.Attribute */ +.nb { color: #007020 } /* Name.Builtin */ +.nc { color: #BB0066; font-weight: bold } /* Name.Class */ +.no { color: #003366; font-weight: bold } /* Name.Constant */ +.nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.ni { color: #880000; font-weight: bold } /* Name.Entity */ +.ne { color: #FF0000; font-weight: bold } /* Name.Exception */ +.nf { color: #0066BB; font-weight: bold } /* Name.Function */ +.nl { color: #997700; font-weight: bold } /* Name.Label */ +.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.nt { color: #007700 } /* Name.Tag */ +.nv { color: #996633 } /* Name.Variable */ +.ow { color: #000000; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */ +.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */ +.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */ +.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ +.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */ +.sb { background-color: #fff0f0 } /* Literal.String.Backtick */ +.sc { color: #0044DD } /* Literal.String.Char */ +.sd { color: #DD4422 } /* Literal.String.Doc */ +.s2 { background-color: #fff0f0 } /* Literal.String.Double */ +.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ +.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ +.si { background-color: #eeeeee } /* Literal.String.Interpol */ +.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */ +.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ +.s1 { background-color: #fff0f0 } /* Literal.String.Single */ +.ss { color: #AA6600 } /* Literal.String.Symbol */ +.bp { color: #007020 } /* Name.Builtin.Pseudo */ +.vc { color: #336699 } /* Name.Variable.Class */ +.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */ +.vi { color: #3333BB } /* Name.Variable.Instance */ +.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ Index: llvm/trunk/test/tools/opt-viewer/basic.test =================================================================== --- llvm/trunk/test/tools/opt-viewer/basic.test +++ llvm/trunk/test/tools/opt-viewer/basic.test @@ -0,0 +1,5 @@ +RUN: %opt-viewer -s %p/Inputs -o %t %p/Inputs/basic/or.yaml +RUN: diff %p/Outputs/basic/index.html %t/index.html +RUN: diff %p/Outputs/basic/basic_or.h.html %t/basic_or.h.html +RUN: diff %p/Outputs/basic/basic_or.c.html %t/basic_or.c.html +RUN: ls %t/style.css Index: llvm/trunk/test/tools/opt-viewer/lit.local.cfg =================================================================== --- llvm/trunk/test/tools/opt-viewer/lit.local.cfg +++ llvm/trunk/test/tools/opt-viewer/lit.local.cfg @@ -0,0 +1,2 @@ +if 'have_opt_viewer_modules' not in config.available_features: + config.unsupported = True