1 /** 2 * Run the C preprocessor on a C source file. 3 * 4 * Specification: C11 5 * 6 * Copyright: Copyright (C) 2022-2023 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cpreprocess.d, _cpreprocess.d) 10 * Documentation: https://dlang.org/phobos/dmd_cpreprocess.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cpreprocess.d 12 */ 13 14 module dmd.cpreprocess; 15 16 import core.stdc.stdio; 17 import core.stdc.stdlib; 18 19 import dmd.errors; 20 import dmd.globals; 21 import dmd.link; 22 import dmd.location; 23 import dmd.target; 24 import dmd.vsoptions; 25 26 import dmd.common.outbuffer; 27 28 import dmd.root.array; 29 import dmd.root.filename; 30 import dmd.root.string; 31 32 // Use default for other versions 33 version (Posix) version = runPreprocessor; 34 version (Windows) version = runPreprocessor; 35 36 37 /*************************************** 38 * Preprocess C file. 39 * Params: 40 * csrcfile = C file to be preprocessed, with .c or .h extension 41 * loc = The source location where preprocess is requested from 42 * ifile = set to true if an output file was written 43 * defines = buffer to append any `#define` and `#undef` lines encountered to 44 * Result: 45 * filename of output 46 */ 47 extern (C++) 48 FileName preprocess(FileName csrcfile, ref const Loc loc, out bool ifile, OutBuffer* defines) 49 { 50 /* Look for "importc.h" by searching along import path. 51 * It should be in the same place as "object.d" 52 */ 53 const(char)* importc_h; 54 55 foreach (entry; (global.path ? (*global.path)[] : null)) 56 { 57 auto f = FileName.combine(entry, "importc.h"); 58 if (FileName.exists(f) == 1) 59 { 60 importc_h = f; 61 break; 62 } 63 FileName.free(f); 64 } 65 66 if (importc_h) 67 { 68 if (global.params.v.verbose) 69 message("include %s", importc_h); 70 } 71 else 72 { 73 error(loc, "cannot find \"importc.h\" along import path"); 74 fatal(); 75 } 76 77 //printf("preprocess %s\n", csrcfile.toChars()); 78 version (runPreprocessor) 79 { 80 /* 81 To get sppn.exe: http://ftp.digitalmars.com/sppn.zip 82 To get the dmc C headers, dmc will need to be installed: 83 http://ftp.digitalmars.com/Digital_Mars_C++/Patch/dm857c.zip 84 */ 85 const name = FileName.name(csrcfile.toString()); 86 const ext = FileName.ext(name); 87 assert(ext); 88 const ifilename = FileName.addExt(name[0 .. name.length - (ext.length + 1)], i_ext); 89 const command = global.params.cpp ? toDString(global.params.cpp) : cppCommand(); 90 auto status = runPreprocessor(command, csrcfile.toString(), importc_h, global.params.cppswitches, ifilename, defines); 91 if (status) 92 { 93 error(loc, "C preprocess command %.*s failed for file %s, exit status %d\n", 94 cast(int)command.length, command.ptr, csrcfile.toChars(), status); 95 fatal(); 96 } 97 //printf("C preprocess succeeded %s\n", ifilename.ptr); 98 ifile = true; 99 return FileName(ifilename); 100 } 101 else 102 return csrcfile; // no-op 103 } 104 105 private const(char)[] cppCommand() 106 { 107 if (auto p = getenv("CPPCMD")) 108 return toDString(p); 109 110 version (Windows) 111 { 112 if (target.objectFormat() == Target.ObjectFormat.coff) 113 { 114 VSOptions vsopt; 115 vsopt.initialize(); 116 auto path = vsopt.compilerPath(target.isX86_64); 117 return toDString(path); 118 } 119 if (target.objectFormat() == Target.ObjectFormat.omf) 120 { 121 return "sppn.exe"; 122 } 123 // Perhaps we are cross-compiling. 124 return "cpp"; 125 } 126 else version (OpenBSD) 127 { 128 // On OpenBSD, we need to use the actual binary /usr/libexec/cpp 129 // rather than the shell script wrapper /usr/bin/cpp ... 130 // Turns out the shell script doesn't really understand -o 131 return "/usr/libexec/cpp"; 132 } 133 else version (OSX) 134 { 135 return "clang"; 136 } 137 else 138 { 139 return "cpp"; 140 } 141 }