1 /** 2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 10 This module can deduce the system compiler flags, if possible, from the 11 compiler specified in a CompileCommand. 12 13 The module assumes that during an execution the system flags for a compiler do 14 not change thus they can be cached. This avoids having to invoke the compiler 15 more than necessary. 16 17 This module exists for those times that: 18 * a cross-compiler which uses other system headers than the hosts system 19 compiler. E.g. clang-tidy do not know *what* these are thus this module 20 discoveres them and provide them. 21 * multiple compiler versions are used in a build and each have different 22 headers. 23 */ 24 module dextool.compilation_db.system_compiler; 25 26 import logger = std.experimental.logger; 27 28 import dextool.compilation_db : CompileCommand; 29 30 version (unittest) { 31 import unit_threaded : shouldBeIn, shouldEqual; 32 } 33 34 @safe: 35 36 struct Compiler { 37 string value; 38 alias value this; 39 } 40 41 struct SystemIncludePath { 42 string value; 43 alias value this; 44 } 45 46 /** Execute and inspect the compiler for the system includes. 47 * 48 * Note that how the compilers are inspected is hard coded. 49 */ 50 SystemIncludePath[] deduceSystemIncludes(ref CompileCommand cmd, const Compiler compiler) { 51 import std.process : execute; 52 53 if (cmd.command.length == 0 || compiler.length == 0) 54 return null; 55 56 if (auto v = compiler in cacheSysIncludes) { 57 return *v; 58 } 59 60 auto args = systemCompilerArg(cmd, compiler); 61 62 auto res = execute(args); 63 if (res.status != 0) { 64 logger.tracef("Failed to inspect the compiler for system includes: %-(%s %)", args); 65 logger.trace(res.output); 66 return null; 67 } 68 69 auto incls = parseCompilerOutput(res.output); 70 cacheSysIncludes[compiler] = incls; 71 72 return incls; 73 } 74 75 private: 76 77 string[] systemCompilerArg(ref CompileCommand cmd, const Compiler compiler) { 78 string[] args = ["-v", "/dev/null", "-fsyntax-only"]; 79 if (auto v = language(compiler, cmd.command)) { 80 args = [v] ~ args; 81 } 82 if (auto v = sysroot(cmd.command)) { 83 args ~= v; 84 } 85 return [compiler.value] ~ args; 86 } 87 88 SystemIncludePath[] parseCompilerOutput(T)(T output) { 89 import std.algorithm : countUntil, map; 90 import std.array : array; 91 import std.string : stripLeft, splitLines; 92 93 auto lines = output.splitLines; 94 const start = lines.countUntil("#include <...> search starts here:") + 1; 95 const end = lines.countUntil("End of search list."); 96 if (start == 0 || end == 0) 97 return null; 98 99 auto incls = lines[start .. end].map!(a => SystemIncludePath(a.stripLeft)).array; 100 101 return incls; 102 } 103 104 SystemIncludePath[][Compiler] cacheSysIncludes; 105 106 // assumes that compilers adher to the gcc and llvm commands use of --sysroot / -isysroot. 107 // depends on the fact that CompileCommand.Command always splits e.g. a --isysroot=foo to ["--sysroot", "foo"]. 108 string[] sysroot(ref CompileCommand.Command cmd) { 109 import std.algorithm : countUntil; 110 import std.string : startsWith; 111 112 auto index = cmd.countUntil!(a => a.startsWith("--sysroot")) + 1; 113 if (index > 0 && (index + 1) < cmd.length) 114 return cmd[index .. index + 1]; 115 116 index = cmd.countUntil!(a => a.startsWith("-isysroot")) + 1; 117 if (index > 0 && (index + 1) < cmd.length) 118 return cmd[index .. index + 1]; 119 120 return null; 121 } 122 123 // assumes that compilers adher to the gcc and llvm commands of using -xLANG 124 string language(Compiler compiler, ref CompileCommand.Command cmd) { 125 import std.algorithm : countUntil; 126 import std.path : baseName; 127 import std.string : startsWith; 128 import std.typecons : No; 129 130 auto index = cmd.countUntil!(a => a.startsWith("-x")) + 1; 131 if (index > 0) 132 return cmd[index]; 133 134 switch (compiler.baseName) { 135 case "cc": 136 case "clang": 137 case "gcc": 138 return "-xc"; 139 case "c++": 140 case "clang++": 141 case "g++": 142 return "-xc++"; 143 default: 144 } 145 146 return null; 147 } 148 149 @("shall parse the system flags") 150 unittest { 151 import std.typecons : Tuple; 152 153 // arrange 154 immutable compiler_output = `Using built-in specs. 155 COLLECT_GCC=gcc 156 COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper 157 OFFLOAD_TARGET_NAMES=nvptx-none 158 OFFLOAD_TARGET_DEFAULT=1 159 Target: x86_64-linux-gnu 160 Configured with: ../src/configure -v --with-pkgversion='Ubuntu 7.3.0-27ubuntu1~18.04' --with-bugurl=file:///usr/share/doc/gcc-7/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++ --prefix=/usr --with 161 -gcc-major-version-only --program-suffix=-7 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-n 162 ls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-d 163 efault-pie --with-system-zlib --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic 164 --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu 165 Thread model: posix 166 gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) 167 COLLECT_GCC_OPTIONS='-v' '-fsyntax-only' '-mtune=generic' '-march=x86-64' 168 /usr/lib/gcc/x86_64-linux-gnu/7/cc1 -quiet -v -imultiarch x86_64-linux-gnu /dev/null -quiet -dumpbase null -mtune=generic -march=x86-64 -auxbase null -version -fsyntax-only -o /dev/null -fstack-protector-strong -Wformat 169 -Wformat-security 170 GNU C11 (Ubuntu 7.3.0-27ubuntu1~18.04) version 7.3.0 (x86_64-linux-gnu) 171 compiled by GNU C version 7.3.0, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0, isl version isl-0.19-GMP 172 173 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 174 ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" 175 ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/7/../../../../x86_64-linux-gnu/include" 176 #include "..." search starts here: 177 #include <...> search starts here: 178 /usr/lib/gcc/x86_64-linux-gnu/7/include/foo 179 /usr/local/include 180 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed 181 /usr/include/x86_64-linux-gnu 182 /usr/include 183 End of search list. 184 GNU C11 (Ubuntu 7.3.0-27ubuntu1~18.04) version 7.3.0 (x86_64-linux-gnu) 185 compiled by GNU C version 7.3.0, GMP version 6.1.2, MPFR version 4.0.1, MPC version 1.1.0, isl version isl-0.19-GMP 186 187 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 188 Compiler executable checksum: c8081a99abb72bbfd9129549110a350c 189 COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/ 190 LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/7/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/us 191 r/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/7/../../../:/lib/:/usr/lib/ 192 COLLECT_GCC_OPTIONS='-v' '-fsyntax-only' '-mtune=generic' '-march=x86-64'`; 193 194 // act 195 auto sysflags = parseCompilerOutput(compiler_output); 196 197 // assert 198 "/usr/lib/gcc/x86_64-linux-gnu/7/include/foo".shouldBeIn(sysflags); 199 "/usr/local/include".shouldBeIn(sysflags); 200 "/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed".shouldBeIn(sysflags); 201 "/usr/include/x86_64-linux-gnu".shouldBeIn(sysflags); 202 "/usr/include".shouldBeIn(sysflags); 203 }